Working on IGD
parent
3db766e2ec
commit
20879726b0
|
@ -4,52 +4,34 @@ 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/
|
||||
"""
|
||||
# XXX TODO:
|
||||
# + flow:
|
||||
# + discovery
|
||||
# + adding device
|
||||
# + removing device
|
||||
# - configured:
|
||||
# - adding
|
||||
# - sensors:
|
||||
# + adding
|
||||
# + handle overflow
|
||||
# - removing
|
||||
# - port forward:
|
||||
# - adding
|
||||
# - removing
|
||||
# - shutdown
|
||||
|
||||
|
||||
import asyncio
|
||||
from ipaddress import IPv4Address
|
||||
from ipaddress import ip_address
|
||||
import aiohttp
|
||||
import asyncio
|
||||
|
||||
import aiohttp
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.const import CONF_URL
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers import discovery
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||
from homeassistant.util import get_local_ip
|
||||
from homeassistant.components.discovery import DOMAIN as DISCOVERY_DOMAIN
|
||||
|
||||
from .config_flow import configured_udns
|
||||
from .const import CONF_PORT_FORWARD, CONF_SENSORS
|
||||
from .const import CONF_ENABLE_PORT_MAPPING, CONF_ENABLE_SENSORS
|
||||
from .const import DOMAIN
|
||||
from .const import LOGGER as _LOGGER
|
||||
|
||||
|
||||
REQUIREMENTS = ['async-upnp-client==0.12.4']
|
||||
DEPENDENCIES = ['http']
|
||||
DEPENDENCIES = ['http'] # ,'discovery']
|
||||
|
||||
CONF_LOCAL_IP = 'local_ip'
|
||||
CONF_ENABLE_PORT_MAPPING = 'port_mapping'
|
||||
CONF_PORTS = 'ports'
|
||||
CONF_UNITS = 'unit'
|
||||
CONF_HASS = 'hass'
|
||||
|
@ -66,17 +48,16 @@ UNITS = {
|
|||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Required(CONF_URL): cv.url,
|
||||
vol.Optional(CONF_ENABLE_PORT_MAPPING, default=True): cv.boolean,
|
||||
vol.Optional(CONF_UNITS, default="MBytes"): vol.In(UNITS),
|
||||
vol.Optional(CONF_ENABLE_PORT_MAPPING, default=False): cv.boolean,
|
||||
vol.Optional(CONF_ENABLE_SENSORS, default=True): cv.boolean,
|
||||
vol.Optional(CONF_LOCAL_IP): vol.All(ip_address, cv.string),
|
||||
vol.Optional(CONF_PORTS):
|
||||
vol.Schema({vol.Any(CONF_HASS, cv.positive_int): cv.positive_int})
|
||||
vol.Optional(CONF_UNITS, default="MBytes"): vol.In(UNITS),
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
async def _async_create_igd_device(hass: HomeAssistantType, ssdp_description: str):
|
||||
async def _async_create_igd_device(hass: HomeAssistantType,
|
||||
ssdp_description: str):
|
||||
"""."""
|
||||
# build requester
|
||||
from async_upnp_client.aiohttp import AiohttpSessionRequester
|
||||
|
@ -111,19 +92,22 @@ def _get_device(hass: HomeAssistantType, udn):
|
|||
return hass.data[DOMAIN]['devices'][udn]
|
||||
|
||||
|
||||
async def _async_create_port_forward(hass: HomeAssistantType, igd_device):
|
||||
"""Create a port forward."""
|
||||
_LOGGER.debug('Creating port forward: %s', igd_device)
|
||||
|
||||
async def _async_add_port_mapping(hass: HomeAssistantType,
|
||||
igd_device,
|
||||
local_ip=None):
|
||||
"""Create a port mapping."""
|
||||
# determine local ip, ensure sane IP
|
||||
local_ip = get_local_ip()
|
||||
if local_ip is None:
|
||||
local_ip = get_local_ip()
|
||||
|
||||
if local_ip == '127.0.0.1':
|
||||
_LOGGER.warning('Could not create port forward, our IP is 127.0.0.1')
|
||||
_LOGGER.warning('Could not create port mapping, our IP is 127.0.0.1')
|
||||
return False
|
||||
local_ip = IPv4Address(local_ip)
|
||||
|
||||
# create port mapping
|
||||
port = hass.http.server_port
|
||||
_LOGGER.debug('Creating port mapping %s:%s:%s (TCP)', port, local_ip, port)
|
||||
await igd_device.async_add_port_mapping(remote_host=None,
|
||||
external_port=port,
|
||||
protocol='TCP',
|
||||
|
@ -136,48 +120,52 @@ async def _async_create_port_forward(hass: HomeAssistantType, igd_device):
|
|||
return True
|
||||
|
||||
|
||||
async def _async_remove_port_forward(hass: HomeAssistantType, igd_device):
|
||||
"""Remove a port forward."""
|
||||
_LOGGER.debug('Removing port forward: %s', igd_device)
|
||||
|
||||
# remove port mapping
|
||||
async def _async_delete_port_mapping(hass: HomeAssistantType, igd_device):
|
||||
"""Remove a port mapping."""
|
||||
port = hass.http.server_port
|
||||
await igd_device.async_remove_port_mapping(remote_host=None,
|
||||
await igd_device.async_delete_port_mapping(remote_host=None,
|
||||
external_port=port,
|
||||
protocol='TCP')
|
||||
|
||||
|
||||
# config
|
||||
async def async_setup(hass: HomeAssistantType, config):
|
||||
async def async_setup(hass: HomeAssistantType, config: ConfigType):
|
||||
"""Register a port mapping for Home Assistant via UPnP."""
|
||||
_LOGGER.debug('async_setup: config: %s', config)
|
||||
conf = config.get(DOMAIN, {})
|
||||
# defaults
|
||||
hass.data[DOMAIN] = {
|
||||
'auto_config': {
|
||||
'active': False,
|
||||
'port_forward': False,
|
||||
'sensors': False,
|
||||
}
|
||||
}
|
||||
|
||||
hass.data[DOMAIN] = hass.data.get(DOMAIN, {})
|
||||
configured = configured_udns(hass)
|
||||
_LOGGER.debug('configured: %s', configured)
|
||||
# ensure sane config
|
||||
if DOMAIN not in config:
|
||||
return False
|
||||
|
||||
# if no ssdp given: take any discovered - by flow - IGD entry
|
||||
# if none discovered, raise PlatformNotReady
|
||||
# if ssdp given: use the SSDP
|
||||
if DISCOVERY_DOMAIN not in config:
|
||||
_LOGGER.warning('IGD needs discovery, please enable it')
|
||||
return False
|
||||
|
||||
igds = [] # XXX TODO
|
||||
for igd_conf in igds:
|
||||
hass.async_add_job(hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={'source': config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
'ssdp_description': igd_conf['ssdp_description'],
|
||||
}
|
||||
))
|
||||
igd_config = config[DOMAIN]
|
||||
if CONF_LOCAL_IP in igd_config:
|
||||
hass.data[DOMAIN]['local_ip'] = igd_config[CONF_LOCAL_IP]
|
||||
|
||||
hass.data[DOMAIN]['auto_config'] = {
|
||||
'active': True,
|
||||
'port_forward': igd_config[CONF_ENABLE_PORT_MAPPING],
|
||||
'sensors': igd_config[CONF_ENABLE_SENSORS],
|
||||
}
|
||||
_LOGGER.debug('Enabled auto_config: %s', hass.data[DOMAIN]['auto_config'])
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# config flow
|
||||
async def async_setup_entry(hass: HomeAssistantType, config_entry: ConfigEntry):
|
||||
async def async_setup_entry(hass: HomeAssistantType,
|
||||
config_entry: ConfigEntry):
|
||||
"""Set up a bridge from a config entry."""
|
||||
_LOGGER.debug('async_setup_entry: title: %s, data: %s', config_entry.title, config_entry.data)
|
||||
|
||||
data = config_entry.data
|
||||
ssdp_description = data['ssdp_description']
|
||||
|
||||
|
@ -189,44 +177,49 @@ async def async_setup_entry(hass: HomeAssistantType, config_entry: ConfigEntry):
|
|||
|
||||
_store_device(hass, igd_device.udn, igd_device)
|
||||
|
||||
# port forward
|
||||
if data.get(CONF_PORT_FORWARD):
|
||||
await _async_create_port_forward(hass, igd_device)
|
||||
# port mapping
|
||||
if data.get(CONF_ENABLE_PORT_MAPPING):
|
||||
local_ip = hass.data[DOMAIN].get('local_ip')
|
||||
await _async_add_port_mapping(hass, igd_device, local_ip=local_ip)
|
||||
|
||||
# sensors
|
||||
if data.get(CONF_SENSORS):
|
||||
if data.get(CONF_ENABLE_SENSORS):
|
||||
discovery_info = {
|
||||
'unit': 'MBytes',
|
||||
'udn': data['udn'],
|
||||
}
|
||||
hass_config = config_entry.data
|
||||
hass.async_create_task(discovery.async_load_platform(
|
||||
hass, 'sensor', DOMAIN, discovery_info, hass_config))
|
||||
hass.async_create_task(
|
||||
discovery.async_load_platform(
|
||||
hass, 'sensor', DOMAIN, discovery_info, hass_config))
|
||||
|
||||
async def unload_entry(event):
|
||||
"""Unload entry on quit."""
|
||||
await async_unload_entry(hass, config_entry)
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unload_entry)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistantType, config_entry: ConfigEntry):
|
||||
async def async_unload_entry(hass: HomeAssistantType,
|
||||
config_entry: ConfigEntry):
|
||||
"""Unload a config entry."""
|
||||
_LOGGER.debug('async_unload_entry: title: %s, data: %s', config_entry.title, config_entry.data)
|
||||
data = config_entry.data
|
||||
udn = data['udn']
|
||||
igd_device = _get_device(hass, udn)
|
||||
|
||||
# port forward
|
||||
if data.get(CONF_PORT_FORWARD):
|
||||
_LOGGER.debug('Removing port forward for: %s', igd_device)
|
||||
_async_remove_port_forward(hass, igd_device)
|
||||
igd_device = _get_device(hass, udn)
|
||||
if igd_device is None:
|
||||
return True
|
||||
|
||||
# port mapping
|
||||
if data.get(CONF_ENABLE_PORT_MAPPING):
|
||||
await _async_delete_port_mapping(hass, igd_device)
|
||||
|
||||
# sensors
|
||||
if data.get(CONF_SENSORS):
|
||||
if data.get(CONF_ENABLE_SENSORS):
|
||||
# XXX TODO: remove sensors
|
||||
pass
|
||||
|
||||
_store_device(hass, udn, None)
|
||||
|
||||
return True
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
"""Config flow for IGD."""
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.core import callback
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from .const import CONF_ENABLE_PORT_MAPPING, CONF_ENABLE_SENSORS
|
||||
from .const import DOMAIN
|
||||
from .const import LOGGER as _LOGGER
|
||||
|
||||
|
||||
@callback
|
||||
def configured_udns(hass):
|
||||
|
@ -29,24 +30,23 @@ class IgdFlowHandler(data_entry_flow.FlowHandler):
|
|||
@property
|
||||
def _discovereds(self):
|
||||
"""Get all discovered entries."""
|
||||
if DOMAIN not in self.hass.data:
|
||||
_LOGGER.debug('DOMAIN not in hass.data')
|
||||
if 'discovered' not in self.hass.data.get(DOMAIN, {}):
|
||||
_LOGGER.debug('discovered not in hass.data[DOMAIN]')
|
||||
|
||||
return self.hass.data.get(DOMAIN, {}).get('discovered', {})
|
||||
|
||||
def _store_discovery_info(self, discovery_info):
|
||||
"""Add discovery info."""
|
||||
udn = discovery_info['udn']
|
||||
if DOMAIN not in self.hass.data:
|
||||
_LOGGER.debug('DOMAIN not in hass.data')
|
||||
self.hass.data[DOMAIN] = self.hass.data.get(DOMAIN, {})
|
||||
if 'discovered' not in self.hass.data[DOMAIN]:
|
||||
_LOGGER.debug('Creating new discovered: %s', self.hass.data[DOMAIN])
|
||||
self.hass.data[DOMAIN]['discovered'] = self.hass.data[DOMAIN].get('discovered', {})
|
||||
self.hass.data[DOMAIN]['discovered'] = \
|
||||
self.hass.data[DOMAIN].get('discovered', {})
|
||||
self.hass.data[DOMAIN]['discovered'][udn] = discovery_info
|
||||
|
||||
def _auto_config_settings(self):
|
||||
"""Check if auto_config has been enabled."""
|
||||
self.hass.data[DOMAIN] = self.hass.data.get(DOMAIN, {})
|
||||
return self.hass.data[DOMAIN].get('auto_config', {
|
||||
'active': False,
|
||||
})
|
||||
|
||||
async def async_step_discovery(self, discovery_info):
|
||||
"""
|
||||
Handle a discovered IGD.
|
||||
|
@ -54,32 +54,33 @@ class IgdFlowHandler(data_entry_flow.FlowHandler):
|
|||
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.
|
||||
"""
|
||||
_LOGGER.debug('async_step_discovery %s: %s', id(self), discovery_info)
|
||||
|
||||
# ensure not already discovered/configured
|
||||
udn = discovery_info['udn']
|
||||
if udn in configured_udns(self.hass):
|
||||
_LOGGER.debug('Already configured: %s', discovery_info)
|
||||
return self.async_abort(reason='already_configured')
|
||||
|
||||
# store discovered device
|
||||
self._store_discovery_info(discovery_info)
|
||||
|
||||
# abort --> not showing up in discovered things
|
||||
# return self.async_abort(reason='user_input_required')
|
||||
# auto config?
|
||||
auto_config = self._auto_config_settings()
|
||||
if auto_config['active']:
|
||||
import_info = {
|
||||
'igd_host': discovery_info['host'],
|
||||
'sensors': auto_config['sensors'],
|
||||
'port_forward': auto_config['port_forward'],
|
||||
}
|
||||
|
||||
return await self._async_save_entry(import_info)
|
||||
|
||||
# user -> showing up in discovered things
|
||||
return await self.async_step_user()
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Manual set up."""
|
||||
_LOGGER.debug('async_step_user %s: %s', id(self), user_input)
|
||||
|
||||
# if user input given, handle it
|
||||
user_input = user_input or {}
|
||||
if 'igd_host' in user_input:
|
||||
if not user_input['sensors'] and not user_input['port_forward']:
|
||||
_LOGGER.debug('Aborting, no sensors and no portforward')
|
||||
return self.async_abort(reason='no_sensors_or_port_forward')
|
||||
|
||||
configured_hosts = [
|
||||
|
@ -90,10 +91,9 @@ class IgdFlowHandler(data_entry_flow.FlowHandler):
|
|||
if user_input['igd_host'] in configured_hosts:
|
||||
return self.async_abort(reason='already_configured')
|
||||
|
||||
return await self._async_save(user_input)
|
||||
return await self._async_save_entry(user_input)
|
||||
|
||||
# let user choose from all discovered IGDs
|
||||
_LOGGER.debug('Discovered devices: %s', self._discovereds)
|
||||
igd_hosts = [
|
||||
entry['host']
|
||||
for entry in self._discovereds.values()
|
||||
|
@ -111,10 +111,12 @@ class IgdFlowHandler(data_entry_flow.FlowHandler):
|
|||
})
|
||||
)
|
||||
|
||||
async def _async_save(self, import_info):
|
||||
"""Store IGD as new entry."""
|
||||
_LOGGER.debug('async_step_import %s: %s', id(self), import_info)
|
||||
async def async_step_import(self, import_info):
|
||||
"""Import a new IGD as a config entry."""
|
||||
return await self._async_save_entry(import_info)
|
||||
|
||||
async def _async_save_entry(self, import_info):
|
||||
"""Store IGD as new entry."""
|
||||
# ensure we know the host
|
||||
igd_host = import_info['igd_host']
|
||||
discovery_infos = [info
|
||||
|
@ -129,7 +131,7 @@ class IgdFlowHandler(data_entry_flow.FlowHandler):
|
|||
data={
|
||||
'ssdp_description': discovery_info['ssdp_description'],
|
||||
'udn': discovery_info['udn'],
|
||||
'sensors': import_info['sensors'],
|
||||
'port_forward': import_info['port_forward'],
|
||||
CONF_ENABLE_SENSORS: import_info['sensors'],
|
||||
CONF_ENABLE_PORT_MAPPING: import_info['port_forward'],
|
||||
},
|
||||
)
|
||||
|
|
|
@ -3,5 +3,5 @@ import logging
|
|||
|
||||
DOMAIN = 'igd'
|
||||
LOGGER = logging.getLogger('homeassistant.components.igd')
|
||||
CONF_PORT_FORWARD = 'port_forward'
|
||||
CONF_SENSORS = 'sensors'
|
||||
CONF_ENABLE_PORT_MAPPING = 'port_forward'
|
||||
CONF_ENABLE_SENSORS = 'sensors'
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
"""
|
||||
Support for UPnP Sensors (IGD).
|
||||
Support for IGD Sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.upnp/
|
||||
https://home-assistant.io/components/sensor.igd/
|
||||
"""
|
||||
# pylint: disable=invalid-name
|
||||
import logging
|
||||
|
||||
from homeassistant.components import history
|
||||
from homeassistant.components.igd import DOMAIN, UNITS
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['igd', 'history']
|
||||
|
@ -98,9 +100,6 @@ class IGDSensor(Entity):
|
|||
|
||||
self._handle_new_value(new_value)
|
||||
|
||||
# _LOGGER.debug('Removing self: %s', self)
|
||||
# await self.async_remove() # XXX TODO: does not remove from the UI
|
||||
|
||||
@property
|
||||
def _last_state(self):
|
||||
"""Get the last state reported to hass."""
|
||||
|
@ -126,17 +125,11 @@ class IGDSensor(Entity):
|
|||
try:
|
||||
state = coercer(float(last_state.state)) * self.unit_factor
|
||||
except ValueError:
|
||||
_LOGGER.debug('%s: value error, coercer: %s, state: %s', self.entity_id, coercer, last_state.state)
|
||||
raise
|
||||
state = coercer(0.0)
|
||||
|
||||
return state
|
||||
|
||||
def _handle_new_value(self, new_value):
|
||||
_LOGGER.debug('%s: handle_new_value: state: %s, new_value: %s, last_value: %s',
|
||||
self.entity_id, self._state, new_value, self._last_value)
|
||||
|
||||
# ❯❯❯ upnp-client --debug --pprint --device http://192.168.178.1/RootDevice.xml call-action WANCIFC/GetTotalBytesReceived
|
||||
if self.entity_id is None:
|
||||
# don't know our entity ID yet, do nothing but store value
|
||||
self._last_value = new_value
|
||||
|
@ -161,7 +154,8 @@ class IGDSensor(Entity):
|
|||
if new_value >= 0:
|
||||
diff += new_value
|
||||
else:
|
||||
# some devices don't overflow and start at 0, but somewhere to -2**32
|
||||
# some devices don't overflow and start at 0,
|
||||
# but somewhere to -2**32
|
||||
diff += new_value - -OVERFLOW_AT
|
||||
|
||||
self._state += diff
|
||||
|
|
|
@ -141,6 +141,7 @@ apns2==0.3.0
|
|||
# homeassistant.components.asterisk_mbox
|
||||
asterisk_mbox==0.4.0
|
||||
|
||||
# homeassistant.components.igd
|
||||
# homeassistant.components.media_player.dlna_dmr
|
||||
async-upnp-client==0.12.4
|
||||
|
||||
|
@ -1183,9 +1184,6 @@ pytrafikverket==0.1.5.8
|
|||
# homeassistant.components.device_tracker.unifi
|
||||
pyunifi==2.13
|
||||
|
||||
# homeassistant.components.upnp
|
||||
pyupnp-async==0.1.1.1
|
||||
|
||||
# homeassistant.components.binary_sensor.uptimerobot
|
||||
pyuptimerobot==0.0.5
|
||||
|
||||
|
|
|
@ -177,9 +177,6 @@ pytradfri[async]==5.5.1
|
|||
# homeassistant.components.device_tracker.unifi
|
||||
pyunifi==2.13
|
||||
|
||||
# homeassistant.components.upnp
|
||||
pyupnp-async==0.1.1.1
|
||||
|
||||
# homeassistant.components.notify.html5
|
||||
pywebpush==1.6.0
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
"""Tests for the IGD component."""
|
||||
"""Tests for the IGD component."""
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
"""Tests for IGD config flow."""
|
||||
|
||||
from homeassistant.components import igd
|
||||
from homeassistant.components.igd import config_flow as igd_config_flow
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_flow_none_discovered(hass):
|
||||
"""Test no device discovered flow."""
|
||||
flow = igd.config_flow.IgdFlowHandler()
|
||||
flow = igd_config_flow.IgdFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
result = await flow.async_step_user()
|
||||
|
@ -17,7 +18,7 @@ async def test_flow_none_discovered(hass):
|
|||
|
||||
async def test_flow_already_configured(hass):
|
||||
"""Test device already configured flow."""
|
||||
flow = igd.config_flow.IgdFlowHandler()
|
||||
flow = igd_config_flow.IgdFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
# discovered device
|
||||
|
@ -48,7 +49,7 @@ async def test_flow_already_configured(hass):
|
|||
|
||||
async def test_flow_no_sensors_no_port_forward(hass):
|
||||
"""Test single device, no sensors, no port_forward."""
|
||||
flow = igd.config_flow.IgdFlowHandler()
|
||||
flow = igd_config_flow.IgdFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
# discovered device
|
||||
|
@ -79,7 +80,7 @@ async def test_flow_no_sensors_no_port_forward(hass):
|
|||
|
||||
async def test_flow_discovered_form(hass):
|
||||
"""Test single device discovered, show form flow."""
|
||||
flow = igd.config_flow.IgdFlowHandler()
|
||||
flow = igd_config_flow.IgdFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
# discovered device
|
||||
|
@ -100,7 +101,7 @@ async def test_flow_discovered_form(hass):
|
|||
|
||||
async def test_flow_two_discovered_form(hass):
|
||||
"""Test single device discovered, show form flow."""
|
||||
flow = igd.config_flow.IgdFlowHandler()
|
||||
flow = igd_config_flow.IgdFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
# discovered device
|
||||
|
@ -135,18 +136,18 @@ async def test_flow_two_discovered_form(hass):
|
|||
|
||||
|
||||
async def test_config_entry_created(hass):
|
||||
flow = igd.config_flow.IgdFlowHandler()
|
||||
"""Test config entry is created."""
|
||||
flow = igd_config_flow.IgdFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
# discovered device
|
||||
udn = 'uuid:device_1'
|
||||
hass.data[igd.DOMAIN] = {
|
||||
'discovered': {
|
||||
udn: {
|
||||
'uuid:device_1': {
|
||||
'name': 'Test device 1',
|
||||
'host': '192.168.1.1',
|
||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
||||
'udn': udn,
|
||||
'udn': 'uuid:device_1',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -156,6 +157,7 @@ async def test_config_entry_created(hass):
|
|||
'sensors': True,
|
||||
'port_forward': False,
|
||||
})
|
||||
assert result['type'] == 'create_entry'
|
||||
assert result['data'] == {
|
||||
'port_forward': False,
|
||||
'sensors': True,
|
||||
|
@ -163,3 +165,67 @@ async def test_config_entry_created(hass):
|
|||
'udn': 'uuid:device_1',
|
||||
}
|
||||
assert result['title'] == 'Test device 1'
|
||||
|
||||
|
||||
async def test_flow_discovery_auto_config_sensors(hass):
|
||||
"""Test creation of device with auto_config."""
|
||||
flow = igd_config_flow.IgdFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
# auto_config active
|
||||
hass.data[igd.DOMAIN] = {
|
||||
'auto_config': {
|
||||
'active': True,
|
||||
'port_forward': False,
|
||||
'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'] == {
|
||||
'port_forward': False,
|
||||
'sensors': True,
|
||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
||||
'udn': 'uuid:device_1',
|
||||
}
|
||||
assert result['title'] == 'Test device 1'
|
||||
|
||||
|
||||
async def test_flow_discovery_auto_config_sensors_port_forward(hass):
|
||||
"""Test creation of device with auto_config, with port forward."""
|
||||
flow = igd_config_flow.IgdFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
# auto_config active, with port_forward
|
||||
hass.data[igd.DOMAIN] = {
|
||||
'auto_config': {
|
||||
'active': True,
|
||||
'port_forward': True,
|
||||
'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'] == {
|
||||
'port_forward': True,
|
||||
'sensors': True,
|
||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
||||
'udn': 'uuid:device_1',
|
||||
}
|
||||
assert result['title'] == 'Test device 1'
|
||||
|
|
|
@ -10,9 +10,96 @@ from tests.common import MockConfigEntry
|
|||
from tests.common import mock_coro
|
||||
|
||||
|
||||
async def test_async_setup_entry_port_forward_created(hass):
|
||||
"""Test async_setup_entry."""
|
||||
async def test_async_setup_no_auto_config(hass):
|
||||
"""Test async_setup."""
|
||||
# setup component, enable auto_config
|
||||
await async_setup_component(hass, 'igd')
|
||||
|
||||
assert hass.data[igd.DOMAIN]['auto_config'] == {
|
||||
'active': False,
|
||||
'port_forward': False,
|
||||
'sensors': False,
|
||||
}
|
||||
|
||||
|
||||
async def test_async_setup_auto_config(hass):
|
||||
"""Test async_setup."""
|
||||
# setup component, enable auto_config
|
||||
await async_setup_component(hass, 'igd', {'igd': {}, 'discovery': {}})
|
||||
|
||||
assert hass.data[igd.DOMAIN]['auto_config'] == {
|
||||
'active': True,
|
||||
'port_forward': False,
|
||||
'sensors': True,
|
||||
}
|
||||
|
||||
|
||||
async def test_async_setup_auto_config_port_forward(hass):
|
||||
"""Test async_setup."""
|
||||
# setup component, enable auto_config
|
||||
await async_setup_component(hass, 'igd', {
|
||||
'igd': {'port_forward': True},
|
||||
'discovery': {}})
|
||||
|
||||
assert hass.data[igd.DOMAIN]['auto_config'] == {
|
||||
'active': True,
|
||||
'port_forward': True,
|
||||
'sensors': True,
|
||||
}
|
||||
|
||||
|
||||
async def test_async_setup_auto_config_no_sensors(hass):
|
||||
"""Test async_setup."""
|
||||
# setup component, enable auto_config
|
||||
await async_setup_component(hass, 'igd', {
|
||||
'igd': {'sensors': False},
|
||||
'discovery': {}})
|
||||
|
||||
assert hass.data[igd.DOMAIN]['auto_config'] == {
|
||||
'active': True,
|
||||
'port_forward': False,
|
||||
'sensors': False,
|
||||
}
|
||||
|
||||
|
||||
async def test_async_setup_entry_default(hass):
|
||||
"""Test async_setup_entry."""
|
||||
udn = 'uuid:device_1'
|
||||
entry = MockConfigEntry(domain=igd.DOMAIN, data={
|
||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
||||
'udn': udn,
|
||||
'sensors': True,
|
||||
'port_forward': False,
|
||||
})
|
||||
|
||||
# ensure hass.http is available
|
||||
await async_setup_component(hass, 'igd')
|
||||
|
||||
# mock async_upnp_client.igd.IgdDevice
|
||||
mock_igd_device = MagicMock()
|
||||
mock_igd_device.udn = udn
|
||||
mock_igd_device.async_add_port_mapping.return_value = mock_coro()
|
||||
mock_igd_device.async_delete_port_mapping.return_value = mock_coro()
|
||||
with patch.object(igd, '_async_create_igd_device') as mock_create_device:
|
||||
mock_create_device.return_value = mock_coro(
|
||||
return_value=mock_igd_device)
|
||||
with patch('homeassistant.components.igd.get_local_ip',
|
||||
return_value='192.168.1.10'):
|
||||
assert await igd.async_setup_entry(hass, entry) is True
|
||||
|
||||
# ensure device is stored/used
|
||||
assert hass.data[igd.DOMAIN]['devices'][udn] == mock_igd_device
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.data[igd.DOMAIN]['devices'][udn] is None
|
||||
assert len(mock_igd_device.async_add_port_mapping.mock_calls) == 0
|
||||
assert len(mock_igd_device.async_delete_port_mapping.mock_calls) == 0
|
||||
|
||||
|
||||
async def test_async_setup_entry_port_forward(hass):
|
||||
"""Test async_setup_entry."""
|
||||
udn = 'uuid:device_1'
|
||||
entry = MockConfigEntry(domain=igd.DOMAIN, data={
|
||||
'ssdp_description': 'http://192.168.1.1/desc.xml',
|
||||
|
@ -27,15 +114,20 @@ async def test_async_setup_entry_port_forward_created(hass):
|
|||
mock_igd_device = MagicMock()
|
||||
mock_igd_device.udn = udn
|
||||
mock_igd_device.async_add_port_mapping.return_value = mock_coro()
|
||||
mock_igd_device.async_remove_port_mapping.return_value = mock_coro()
|
||||
mock_igd_device.async_delete_port_mapping.return_value = mock_coro()
|
||||
with patch.object(igd, '_async_create_igd_device') as mock_create_device:
|
||||
mock_create_device.return_value = mock_coro(return_value=mock_igd_device)
|
||||
with patch('homeassistant.components.igd.get_local_ip', return_value='192.168.1.10'):
|
||||
mock_create_device.return_value = mock_coro(
|
||||
return_value=mock_igd_device)
|
||||
with patch('homeassistant.components.igd.get_local_ip',
|
||||
return_value='192.168.1.10'):
|
||||
assert await igd.async_setup_entry(hass, entry) is True
|
||||
|
||||
# ensure device is stored/used
|
||||
assert hass.data[igd.DOMAIN]['devices'][udn] == mock_igd_device
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.data[igd.DOMAIN]['devices'][udn] == mock_igd_device
|
||||
assert hass.data[igd.DOMAIN]['devices'][udn] is None
|
||||
assert len(mock_igd_device.async_add_port_mapping.mock_calls) > 0
|
||||
assert len(mock_igd_device.async_delete_port_mapping.mock_calls) > 0
|
||||
|
|
Loading…
Reference in New Issue