diff --git a/CODEOWNERS b/CODEOWNERS index f5357d1348c..23005cb5273 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -187,6 +187,7 @@ homeassistant/components/life360/* @pnbruckner homeassistant/components/linky/* @Quentame homeassistant/components/linux_battery/* @fabaff homeassistant/components/liveboxplaytv/* @pschmitt +homeassistant/components/local_ip/* @issacg homeassistant/components/logger/* @home-assistant/core homeassistant/components/logi_circle/* @evanjd homeassistant/components/lovelace/* @home-assistant/frontend diff --git a/homeassistant/components/local_ip/__init__.py b/homeassistant/components/local_ip/__init__.py new file mode 100644 index 00000000000..c93b7a5a81b --- /dev/null +++ b/homeassistant/components/local_ip/__init__.py @@ -0,0 +1,42 @@ +"""Get the local IP address of the Home Assistant instance.""" +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.const import CONF_NAME +from homeassistant.core import HomeAssistant +import homeassistant.helpers.config_validation as cv + +DOMAIN = "local_ip" +PLATFORM = "sensor" + +CONFIG_SCHEMA = vol.Schema( + {DOMAIN: vol.Schema({vol.Optional(CONF_NAME, default=DOMAIN): cv.string})}, + extra=vol.ALLOW_EXTRA, +) + + +async def async_setup(hass: HomeAssistant, config: dict): + """Set up local_ip from configuration.yaml.""" + conf = config.get(DOMAIN) + if conf: + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, data=conf, context={"source": config_entries.SOURCE_IMPORT} + ) + ) + + return True + + +async def async_setup_entry(hass: HomeAssistant, entry: config_entries.ConfigEntry): + """Set up local_ip from a config entry.""" + hass.async_create_task( + hass.config_entries.async_forward_entry_setup(entry, PLATFORM) + ) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: config_entries.ConfigEntry): + """Unload a config entry.""" + return await hass.config_entries.async_forward_entry_unload(entry, PLATFORM) diff --git a/homeassistant/components/local_ip/config_flow.py b/homeassistant/components/local_ip/config_flow.py new file mode 100644 index 00000000000..58a666a68f3 --- /dev/null +++ b/homeassistant/components/local_ip/config_flow.py @@ -0,0 +1,34 @@ +"""Config flow for local_ip.""" +import voluptuous as vol + +from homeassistant import config_entries + +from . import DOMAIN + + +class SimpleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for local_ip.""" + + VERSION = 1 + CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL + + async def async_step_user(self, user_input=None): + """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) + + return self.async_show_form( + step_id="user", + data_schema=vol.Schema({vol.Required("name", default=DOMAIN): str}), + errors={}, + ) + + async def async_step_import(self, import_info): + """Handle import from config file.""" + return await self.async_step_user(import_info) diff --git a/homeassistant/components/local_ip/manifest.json b/homeassistant/components/local_ip/manifest.json new file mode 100644 index 00000000000..4e97c32afa0 --- /dev/null +++ b/homeassistant/components/local_ip/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "local_ip", + "name": "Local IP Address", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/local_ip", + "dependencies": [], + "codeowners": ["@issacg"], + "requirements": [] +} diff --git a/homeassistant/components/local_ip/sensor.py b/homeassistant/components/local_ip/sensor.py new file mode 100644 index 00000000000..274a11faec6 --- /dev/null +++ b/homeassistant/components/local_ip/sensor.py @@ -0,0 +1,34 @@ +"""Sensor platform for local_ip.""" + +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import Entity +from homeassistant.util import get_local_ip + + +async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities): + """Set up the platform from config_entry.""" + name = config_entry.data["name"] + async_add_entities([IPSensor(name)], True) + + +class IPSensor(Entity): + """A simple sensor.""" + + def __init__(self, name: str): + """Initialize the sensor.""" + self._state = None + self._name = name + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + def update(self): + """Fetch new state data for the sensor.""" + self._state = get_local_ip() diff --git a/homeassistant/components/local_ip/strings.json b/homeassistant/components/local_ip/strings.json new file mode 100644 index 00000000000..43a88be3325 --- /dev/null +++ b/homeassistant/components/local_ip/strings.json @@ -0,0 +1,16 @@ +{ + "config": { + "title": "Local IP Address", + "step": { + "user": { + "title": "Local IP Address", + "data": { + "name": "Sensor Name" + } + } + }, + "abort": { + "already_configured": "Integration is already configured with an existing sensor with that name" + } + } +} diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 55a4d76fdcd..135fab2b746 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -46,6 +46,7 @@ FLOWS = [ "life360", "lifx", "linky", + "local_ip", "locative", "logi_circle", "luftdaten", diff --git a/tests/components/local_ip/__init__.py b/tests/components/local_ip/__init__.py new file mode 100644 index 00000000000..47e1f70fcf8 --- /dev/null +++ b/tests/components/local_ip/__init__.py @@ -0,0 +1 @@ +"""Tests for the local_ip integration.""" diff --git a/tests/components/local_ip/test_config_flow.py b/tests/components/local_ip/test_config_flow.py new file mode 100644 index 00000000000..f355e5c75b2 --- /dev/null +++ b/tests/components/local_ip/test_config_flow.py @@ -0,0 +1,19 @@ +"""Tests for the local_ip config_flow.""" +from homeassistant.components.local_ip import DOMAIN + + +async def test_config_flow(hass): + """Test we can finish a config flow.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": "user"} + ) + assert result["type"] == "form" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"name": "test"} + ) + assert result["type"] == "create_entry" + + await hass.async_block_till_done() + state = hass.states.get("sensor.test") + assert state diff --git a/tests/components/local_ip/test_init.py b/tests/components/local_ip/test_init.py new file mode 100644 index 00000000000..fb43f06eea2 --- /dev/null +++ b/tests/components/local_ip/test_init.py @@ -0,0 +1,22 @@ +"""Tests for the local_ip component.""" +import pytest + +from homeassistant.components.local_ip import DOMAIN +from homeassistant.setup import async_setup_component +from homeassistant.util import get_local_ip + + +@pytest.fixture(name="config") +def config_fixture(): + """Create hass config fixture.""" + return {DOMAIN: {"name": "test"}} + + +async def test_basic_setup(hass, config): + """Test component setup creates entry from config.""" + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + local_ip = await hass.async_add_executor_job(get_local_ip) + state = hass.states.get("sensor.test") + assert state + assert state.state == local_ip