Add local_ip unique_id & icon and single_instance_allowed (#33483)

* Add config flow + sensor unique_id & icon to local_ip

* single_instance_allowed

* Fix test

* Martin's review

* Name deprecated
This commit is contained in:
Quentame 2020-04-09 16:06:01 +02:00 committed by GitHub
parent dd7fbef948
commit 45b28b8b00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 66 additions and 37 deletions

View file

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Integration is already configured with an existing sensor with that name" "single_instance_allowed": "Only a single configuration of Local IP is allowed."
}, },
"step": { "step": {
"user": { "user": {

View file

@ -1,16 +1,20 @@
"""Get the local IP address of the Home Assistant instance.""" """Get the local IP address of the Home Assistant instance."""
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_NAME from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
DOMAIN = "local_ip" from .const import DOMAIN, PLATFORM
PLATFORM = "sensor"
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{DOMAIN: vol.Schema({vol.Optional(CONF_NAME, default=DOMAIN): cv.string})}, {
DOMAIN: vol.All(
cv.deprecated(CONF_NAME, invalidation_version="0.110"),
vol.Schema({vol.Optional(CONF_NAME, default=DOMAIN): cv.string}),
)
},
extra=vol.ALLOW_EXTRA, extra=vol.ALLOW_EXTRA,
) )
@ -21,14 +25,14 @@ async def async_setup(hass: HomeAssistant, config: dict):
if conf: if conf:
hass.async_create_task( hass.async_create_task(
hass.config_entries.flow.async_init( hass.config_entries.flow.async_init(
DOMAIN, data=conf, context={"source": config_entries.SOURCE_IMPORT} DOMAIN, data=conf, context={"source": SOURCE_IMPORT}
) )
) )
return True return True
async def async_setup_entry(hass: HomeAssistant, entry: config_entries.ConfigEntry): async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up local_ip from a config entry.""" """Set up local_ip from a config entry."""
hass.async_create_task( hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, PLATFORM) hass.config_entries.async_forward_entry_setup(entry, PLATFORM)
@ -37,6 +41,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: config_entries.ConfigEnt
return True return True
async def async_unload_entry(hass: HomeAssistant, entry: config_entries.ConfigEntry): async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry.""" """Unload a config entry."""
return await hass.config_entries.async_forward_entry_unload(entry, PLATFORM) return await hass.config_entries.async_forward_entry_unload(entry, PLATFORM)

View file

@ -1,9 +1,8 @@
"""Config flow for local_ip.""" """Config flow for local_ip."""
import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from . import DOMAIN from .const import DOMAIN
class SimpleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): class SimpleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
@ -14,20 +13,14 @@ class SimpleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, user_input=None): async def async_step_user(self, user_input=None):
"""Handle the initial step.""" """Handle the initial step."""
if user_input is not None:
if any(
user_input["name"] == entry.data["name"]
for entry in self._async_current_entries()
):
return self.async_abort(reason="already_configured")
return self.async_create_entry(title=user_input["name"], data=user_input) if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
return self.async_show_form( if user_input is None:
step_id="user", return self.async_show_form(step_id="user")
data_schema=vol.Schema({vol.Required("name", default=DOMAIN): str}),
errors={}, return self.async_create_entry(title=DOMAIN, data=user_input)
)
async def async_step_import(self, import_info): async def async_step_import(self, import_info):
"""Handle import from config file.""" """Handle import from config file."""

View file

@ -0,0 +1,6 @@
"""Local IP constants."""
DOMAIN = "local_ip"
PLATFORM = "sensor"
SENSOR = "address"

View file

@ -1,20 +1,22 @@
"""Sensor platform for local_ip.""" """Sensor platform for local_ip."""
from homeassistant.core import HomeAssistant from homeassistant.const import CONF_NAME
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.util import get_local_ip from homeassistant.util import get_local_ip
from .const import DOMAIN, SENSOR
async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities):
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the platform from config_entry.""" """Set up the platform from config_entry."""
name = config_entry.data["name"] name = config_entry.data.get(CONF_NAME) or DOMAIN
async_add_entities([IPSensor(name)], True) async_add_entities([IPSensor(name)], True)
class IPSensor(Entity): class IPSensor(Entity):
"""A simple sensor.""" """A simple sensor."""
def __init__(self, name: str): def __init__(self, name):
"""Initialize the sensor.""" """Initialize the sensor."""
self._state = None self._state = None
self._name = name self._name = name
@ -24,11 +26,21 @@ class IPSensor(Entity):
"""Return the name of the sensor.""" """Return the name of the sensor."""
return self._name return self._name
@property
def unique_id(self):
"""Return the unique id of the sensor."""
return SENSOR
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self._state return self._state
@property
def icon(self):
"""Return the icon of the sensor."""
return "mdi:ip"
def update(self): def update(self):
"""Fetch new state data for the sensor.""" """Fetch new state data for the sensor."""
self._state = get_local_ip() self._state = get_local_ip()

View file

@ -10,7 +10,7 @@
} }
}, },
"abort": { "abort": {
"already_configured": "Integration is already configured with an existing sensor with that name" "single_instance_allowed": "Only a single configuration of Local IP is allowed."
} }
} }
} }

View file

@ -1,19 +1,33 @@
"""Tests for the local_ip config_flow.""" """Tests for the local_ip config_flow."""
from homeassistant.components.local_ip import DOMAIN from homeassistant import data_entry_flow
from homeassistant.components.local_ip.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER
from tests.common import MockConfigEntry
async def test_config_flow(hass): async def test_config_flow(hass):
"""Test we can finish a config flow.""" """Test we can finish a config flow."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"} DOMAIN, context={"source": SOURCE_USER}
) )
assert result["type"] == "form" assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
result["flow_id"], {"name": "test"} assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
)
assert result["type"] == "create_entry"
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get("sensor.test") state = hass.states.get(f"sensor.{DOMAIN}")
assert state assert state
async def test_already_setup(hass):
"""Test we abort if already setup."""
MockConfigEntry(domain=DOMAIN, data={},).add_to_hass(hass)
# Should fail, same NAME
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "single_instance_allowed"

View file

@ -9,7 +9,7 @@ from homeassistant.util import get_local_ip
@pytest.fixture(name="config") @pytest.fixture(name="config")
def config_fixture(): def config_fixture():
"""Create hass config fixture.""" """Create hass config fixture."""
return {DOMAIN: {"name": "test"}} return {DOMAIN: {}}
async def test_basic_setup(hass, config): async def test_basic_setup(hass, config):
@ -17,6 +17,6 @@ async def test_basic_setup(hass, config):
assert await async_setup_component(hass, DOMAIN, config) assert await async_setup_component(hass, DOMAIN, config)
await hass.async_block_till_done() await hass.async_block_till_done()
local_ip = await hass.async_add_executor_job(get_local_ip) local_ip = await hass.async_add_executor_job(get_local_ip)
state = hass.states.get("sensor.test") state = hass.states.get(f"sensor.{DOMAIN}")
assert state assert state
assert state.state == local_ip assert state.state == local_ip