From 4b06392442a177e5c0aa5e8cb4f8cfe3383affae Mon Sep 17 00:00:00 2001 From: Kane610 Date: Thu, 26 Apr 2018 23:59:22 +0200 Subject: [PATCH] Zone component config entry support (#14059) * Initial commit * Add error handling to config flow Change unique identifyer to name Clean up hound comments * Ensure hass home zone is created with correct entity id Fix failing tests * Fix rest of tests * Move zone tests to zone folder Create config flow tests * Add possibility to unload entry * Use hass.data instead of globas * Don't calculate configures zones every loop iteration * No need to know about home zone during setup of entry * Only use name as title * Don't cache hass home zone * Add new tests for setup and setup entry * Break out functionality from init to zone.py * Make hass home zone be created directly * Make sure that config flow doesn't override hass home zone * A newline was missing in const * Configured zones shall not be imported Removed config flow import functionality Improved tests --- .../components/device_tracker/__init__.py | 3 +- .../components/device_tracker/icloud.py | 2 +- .../components/zone/.translations/en.json | 21 ++++ homeassistant/components/zone/__init__.py | 93 ++++++++++++++++ homeassistant/components/zone/config_flow.py | 56 ++++++++++ homeassistant/components/zone/const.py | 5 + homeassistant/components/zone/strings.json | 21 ++++ homeassistant/components/{ => zone}/zone.py | 70 +----------- homeassistant/config_entries.py | 1 + homeassistant/helpers/condition.py | 4 +- tests/components/zone/__init__.py | 1 + tests/components/zone/test_config_flow.py | 55 ++++++++++ .../{test_zone.py => zone/test_init.py} | 102 +++++++++++++++--- 13 files changed, 351 insertions(+), 83 deletions(-) create mode 100644 homeassistant/components/zone/.translations/en.json create mode 100644 homeassistant/components/zone/__init__.py create mode 100644 homeassistant/components/zone/config_flow.py create mode 100644 homeassistant/components/zone/const.py create mode 100644 homeassistant/components/zone/strings.json rename homeassistant/components/{ => zone}/zone.py (57%) create mode 100644 tests/components/zone/__init__.py create mode 100644 tests/components/zone/test_config_flow.py rename tests/components/{test_zone.py => zone/test_init.py} (55%) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index b24f7784faf..2f068481953 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -15,6 +15,7 @@ from homeassistant.setup import async_prepare_setup_platform from homeassistant.core import callback from homeassistant.loader import bind_hass from homeassistant.components import group, zone +from homeassistant.components.zone.zone import async_active_zone from homeassistant.config import load_yaml_config_file, async_log_exception from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_per_platform, discovery @@ -541,7 +542,7 @@ class Device(Entity): elif self.location_name: self._state = self.location_name elif self.gps is not None and self.source_type == SOURCE_TYPE_GPS: - zone_state = zone.async_active_zone( + zone_state = async_active_zone( self.hass, self.gps[0], self.gps[1], self.gps_accuracy) if zone_state is None: self._state = STATE_NOT_HOME diff --git a/homeassistant/components/device_tracker/icloud.py b/homeassistant/components/device_tracker/icloud.py index 781e3674550..5d40f5d533a 100644 --- a/homeassistant/components/device_tracker/icloud.py +++ b/homeassistant/components/device_tracker/icloud.py @@ -13,7 +13,7 @@ import voluptuous as vol from homeassistant.const import CONF_USERNAME, CONF_PASSWORD from homeassistant.components.device_tracker import ( PLATFORM_SCHEMA, DOMAIN, ATTR_ATTRIBUTES, ENTITY_ID_FORMAT, DeviceScanner) -from homeassistant.components.zone import active_zone +from homeassistant.components.zone.zone import active_zone from homeassistant.helpers.event import track_utc_time_change import homeassistant.helpers.config_validation as cv from homeassistant.util import slugify diff --git a/homeassistant/components/zone/.translations/en.json b/homeassistant/components/zone/.translations/en.json new file mode 100644 index 00000000000..ff2c7c07c14 --- /dev/null +++ b/homeassistant/components/zone/.translations/en.json @@ -0,0 +1,21 @@ +{ + "config": { + "title": "Zone", + "step": { + "init": { + "title": "Define zone parameters", + "data": { + "name": "Name", + "latitude": "Latitude", + "longitude": "Longitude", + "radius": "Radius", + "passive": "Passive", + "icon": "Icon" + } + } + }, + "error": { + "name_exists": "Name already exists" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zone/__init__.py b/homeassistant/components/zone/__init__.py new file mode 100644 index 00000000000..d3628fd57f3 --- /dev/null +++ b/homeassistant/components/zone/__init__.py @@ -0,0 +1,93 @@ +""" +Support for the definition of zones. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/zone/ +""" + +import logging + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.const import ( + CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, CONF_ICON, CONF_RADIUS) +from homeassistant.helpers import config_per_platform +from homeassistant.helpers.entity import async_generate_entity_id +from homeassistant.util import slugify + +from .config_flow import configured_zones +from .const import CONF_PASSIVE, DOMAIN, HOME_ZONE +from .zone import Zone + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'Unnamed zone' +DEFAULT_PASSIVE = False +DEFAULT_RADIUS = 100 + +ENTITY_ID_FORMAT = 'zone.{}' +ENTITY_ID_HOME = ENTITY_ID_FORMAT.format(HOME_ZONE) + +ICON_HOME = 'mdi:home' +ICON_IMPORT = 'mdi:import' + +# The config that zone accepts is the same as if it has platforms. +PLATFORM_SCHEMA = vol.Schema({ + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Required(CONF_LATITUDE): cv.latitude, + vol.Required(CONF_LONGITUDE): cv.longitude, + vol.Optional(CONF_RADIUS, default=DEFAULT_RADIUS): vol.Coerce(float), + vol.Optional(CONF_PASSIVE, default=DEFAULT_PASSIVE): cv.boolean, + vol.Optional(CONF_ICON): cv.icon, +}, extra=vol.ALLOW_EXTRA) + + +async def async_setup(hass, config): + """Setup configured zones as well as home assistant zone if necessary.""" + if DOMAIN not in hass.data: + hass.data[DOMAIN] = {} + zone_entries = configured_zones(hass) + for _, entry in config_per_platform(config, DOMAIN): + name = slugify(entry[CONF_NAME]) + if name not in zone_entries: + zone = Zone(hass, entry[CONF_NAME], entry[CONF_LATITUDE], + entry[CONF_LONGITUDE], entry.get(CONF_RADIUS), + entry.get(CONF_ICON), entry.get(CONF_PASSIVE)) + zone.entity_id = async_generate_entity_id( + ENTITY_ID_FORMAT, entry[CONF_NAME], None, hass) + hass.async_add_job(zone.async_update_ha_state()) + hass.data[DOMAIN][name] = zone + + if HOME_ZONE not in hass.data[DOMAIN] and HOME_ZONE not in zone_entries: + name = hass.config.location_name + zone = Zone(hass, name, hass.config.latitude, hass.config.longitude, + DEFAULT_RADIUS, ICON_HOME, False) + zone.entity_id = ENTITY_ID_HOME + hass.async_add_job(zone.async_update_ha_state()) + hass.data[DOMAIN][slugify(name)] = zone + + return True + + +async def async_setup_entry(hass, config_entry): + """Set up zone as config entry.""" + entry = config_entry.data + name = entry[CONF_NAME] + zone = Zone(hass, name, entry[CONF_LATITUDE], entry[CONF_LONGITUDE], + entry.get(CONF_RADIUS), entry.get(CONF_ICON), + entry.get(CONF_PASSIVE)) + zone.entity_id = async_generate_entity_id( + ENTITY_ID_FORMAT, name, None, hass) + hass.async_add_job(zone.async_update_ha_state()) + hass.data[DOMAIN][slugify(name)] = zone + return True + + +async def async_unload_entry(hass, config_entry): + """Unload a config entry.""" + zones = hass.data[DOMAIN] + name = slugify(config_entry.data[CONF_NAME]) + zone = zones.pop(name) + await zone.async_remove() + return True diff --git a/homeassistant/components/zone/config_flow.py b/homeassistant/components/zone/config_flow.py new file mode 100644 index 00000000000..5ec955a48d9 --- /dev/null +++ b/homeassistant/components/zone/config_flow.py @@ -0,0 +1,56 @@ +"""Config flow to configure zone component.""" + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant import config_entries, data_entry_flow +from homeassistant.const import ( + CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, CONF_ICON, CONF_RADIUS) +from homeassistant.core import callback +from homeassistant.util import slugify + +from .const import CONF_PASSIVE, DOMAIN, HOME_ZONE + + +@callback +def configured_zones(hass): + """Return a set of the configured hosts.""" + return set((slugify(entry.data[CONF_NAME])) for + entry in hass.config_entries.async_entries(DOMAIN)) + + +@config_entries.HANDLERS.register(DOMAIN) +class ZoneFlowHandler(data_entry_flow.FlowHandler): + """Zone config flow.""" + + VERSION = 1 + + def __init__(self): + """Initialize zone configuration flow.""" + pass + + async def async_step_init(self, user_input=None): + """Handle a flow start.""" + errors = {} + + if user_input is not None: + name = slugify(user_input[CONF_NAME]) + if name not in configured_zones(self.hass) and name != HOME_ZONE: + return self.async_create_entry( + title=user_input[CONF_NAME], + data=user_input, + ) + errors['base'] = 'name_exists' + + return self.async_show_form( + step_id='init', + data_schema=vol.Schema({ + vol.Required(CONF_NAME): str, + vol.Required(CONF_LATITUDE): cv.latitude, + vol.Required(CONF_LONGITUDE): cv.longitude, + vol.Optional(CONF_RADIUS): vol.Coerce(float), + vol.Optional(CONF_ICON): str, + vol.Optional(CONF_PASSIVE): bool, + }), + errors=errors, + ) diff --git a/homeassistant/components/zone/const.py b/homeassistant/components/zone/const.py new file mode 100644 index 00000000000..b69ba67302a --- /dev/null +++ b/homeassistant/components/zone/const.py @@ -0,0 +1,5 @@ +"""Constants for the zone component.""" + +CONF_PASSIVE = 'passive' +DOMAIN = 'zone' +HOME_ZONE = 'home' diff --git a/homeassistant/components/zone/strings.json b/homeassistant/components/zone/strings.json new file mode 100644 index 00000000000..ff2c7c07c14 --- /dev/null +++ b/homeassistant/components/zone/strings.json @@ -0,0 +1,21 @@ +{ + "config": { + "title": "Zone", + "step": { + "init": { + "title": "Define zone parameters", + "data": { + "name": "Name", + "latitude": "Latitude", + "longitude": "Longitude", + "radius": "Radius", + "passive": "Passive", + "icon": "Icon" + } + } + }, + "error": { + "name_exists": "Name already exists" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zone.py b/homeassistant/components/zone/zone.py similarity index 57% rename from homeassistant/components/zone.py rename to homeassistant/components/zone/zone.py index b1a94f3809c..b7c2e9ee858 100644 --- a/homeassistant/components/zone.py +++ b/homeassistant/components/zone/zone.py @@ -1,54 +1,18 @@ -""" -Support for the definition of zones. +"""Component entity and functionality.""" -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/zone/ -""" -import asyncio -import logging - -import voluptuous as vol - -import homeassistant.helpers.config_validation as cv -from homeassistant.const import ( - ATTR_HIDDEN, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_NAME, CONF_LATITUDE, - CONF_LONGITUDE, CONF_ICON, CONF_RADIUS) +from homeassistant.const import ATTR_HIDDEN, ATTR_LATITUDE, ATTR_LONGITUDE +from homeassistant.helpers.entity import Entity from homeassistant.loader import bind_hass -from homeassistant.helpers import config_per_platform -from homeassistant.helpers.entity import Entity, async_generate_entity_id from homeassistant.util.async_ import run_callback_threadsafe from homeassistant.util.location import distance -_LOGGER = logging.getLogger(__name__) +from .const import DOMAIN ATTR_PASSIVE = 'passive' ATTR_RADIUS = 'radius' -CONF_PASSIVE = 'passive' - -DEFAULT_NAME = 'Unnamed zone' -DEFAULT_PASSIVE = False -DEFAULT_RADIUS = 100 -DOMAIN = 'zone' - -ENTITY_ID_FORMAT = 'zone.{}' -ENTITY_ID_HOME = ENTITY_ID_FORMAT.format('home') - -ICON_HOME = 'mdi:home' -ICON_IMPORT = 'mdi:import' - STATE = 'zoning' -# The config that zone accepts is the same as if it has platforms. -PLATFORM_SCHEMA = vol.Schema({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Required(CONF_LATITUDE): cv.latitude, - vol.Required(CONF_LONGITUDE): cv.longitude, - vol.Optional(CONF_RADIUS, default=DEFAULT_RADIUS): vol.Coerce(float), - vol.Optional(CONF_PASSIVE, default=DEFAULT_PASSIVE): cv.boolean, - vol.Optional(CONF_ICON): cv.icon, -}, extra=vol.ALLOW_EXTRA) - @bind_hass def active_zone(hass, latitude, longitude, radius=0): @@ -104,32 +68,6 @@ def in_zone(zone, latitude, longitude, radius=0): return zone_dist - radius < zone.attributes[ATTR_RADIUS] -@asyncio.coroutine -def async_setup(hass, config): - """Set up the zone.""" - entities = set() - tasks = [] - for _, entry in config_per_platform(config, DOMAIN): - name = entry.get(CONF_NAME) - zone = Zone(hass, name, entry[CONF_LATITUDE], entry[CONF_LONGITUDE], - entry.get(CONF_RADIUS), entry.get(CONF_ICON), - entry.get(CONF_PASSIVE)) - zone.entity_id = async_generate_entity_id( - ENTITY_ID_FORMAT, name, entities) - tasks.append(zone.async_update_ha_state()) - entities.add(zone.entity_id) - - if ENTITY_ID_HOME not in entities: - zone = Zone(hass, hass.config.location_name, - hass.config.latitude, hass.config.longitude, - DEFAULT_RADIUS, ICON_HOME, False) - zone.entity_id = ENTITY_ID_HOME - tasks.append(zone.async_update_ha_state()) - - yield from asyncio.wait(tasks, loop=hass.loop) - return True - - class Zone(Entity): """Representation of a Zone.""" diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index b159f01c72b..c23d53f2735 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -129,6 +129,7 @@ HANDLERS = Registry() FLOWS = [ 'deconz', 'hue', + 'zone', ] diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index f8f841cc449..cb577e8a9c7 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -393,8 +393,8 @@ def zone(hass, zone_ent, entity): if latitude is None or longitude is None: return False - return zone_cmp.in_zone(zone_ent, latitude, longitude, - entity.attributes.get(ATTR_GPS_ACCURACY, 0)) + return zone_cmp.zone.in_zone(zone_ent, latitude, longitude, + entity.attributes.get(ATTR_GPS_ACCURACY, 0)) def zone_from_config(config, config_validation=True): diff --git a/tests/components/zone/__init__.py b/tests/components/zone/__init__.py new file mode 100644 index 00000000000..2ba325fce81 --- /dev/null +++ b/tests/components/zone/__init__.py @@ -0,0 +1 @@ +"""Tests for the zone component.""" diff --git a/tests/components/zone/test_config_flow.py b/tests/components/zone/test_config_flow.py new file mode 100644 index 00000000000..d8ee6f7c5c0 --- /dev/null +++ b/tests/components/zone/test_config_flow.py @@ -0,0 +1,55 @@ +"""Tests for zone config flow.""" + +from homeassistant.components.zone import config_flow +from homeassistant.components.zone.const import CONF_PASSIVE, DOMAIN, HOME_ZONE +from homeassistant.const import ( + CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, CONF_ICON, CONF_RADIUS) + +from tests.common import MockConfigEntry + + +async def test_flow_works(hass): + """Test that config flow works.""" + flow = config_flow.ZoneFlowHandler() + flow.hass = hass + + result = await flow.async_step_init(user_input={ + CONF_NAME: 'Name', + CONF_LATITUDE: '1.1', + CONF_LONGITUDE: '2.2', + CONF_RADIUS: '100', + CONF_ICON: 'mdi:home', + CONF_PASSIVE: True + }) + + assert result['type'] == 'create_entry' + assert result['title'] == 'Name' + assert result['data'] == { + CONF_NAME: 'Name', + CONF_LATITUDE: '1.1', + CONF_LONGITUDE: '2.2', + CONF_RADIUS: '100', + CONF_ICON: 'mdi:home', + CONF_PASSIVE: True + } + + +async def test_flow_requires_unique_name(hass): + """Test that config flow verifies that each zones name is unique.""" + MockConfigEntry(domain=DOMAIN, data={ + CONF_NAME: 'Name' + }).add_to_hass(hass) + flow = config_flow.ZoneFlowHandler() + flow.hass = hass + + result = await flow.async_step_init(user_input={CONF_NAME: 'Name'}) + assert result['errors'] == {'base': 'name_exists'} + + +async def test_flow_requires_name_different_from_home(hass): + """Test that config flow verifies that each zones name is unique.""" + flow = config_flow.ZoneFlowHandler() + flow.hass = hass + + result = await flow.async_step_init(user_input={CONF_NAME: HOME_ZONE}) + assert result['errors'] == {'base': 'name_exists'} diff --git a/tests/components/test_zone.py b/tests/components/zone/test_init.py similarity index 55% rename from tests/components/test_zone.py rename to tests/components/zone/test_init.py index 0ea84324362..1c698438f2c 100644 --- a/tests/components/test_zone.py +++ b/tests/components/zone/test_init.py @@ -1,10 +1,42 @@ """Test zone component.""" + import unittest +from unittest.mock import Mock from homeassistant import setup from homeassistant.components import zone from tests.common import get_test_home_assistant +from tests.common import MockConfigEntry + + +async def test_setup_entry_successful(hass): + """Test setup entry is successful.""" + entry = Mock() + entry.data = { + zone.CONF_NAME: 'Test Zone', + zone.CONF_LATITUDE: 1.1, + zone.CONF_LONGITUDE: -2.2, + zone.CONF_RADIUS: 250, + zone.CONF_RADIUS: True + } + hass.data[zone.DOMAIN] = {} + assert await zone.async_setup_entry(hass, entry) is True + assert 'test_zone' in hass.data[zone.DOMAIN] + + +async def test_unload_entry_successful(hass): + """Test unload entry is successful.""" + entry = Mock() + entry.data = { + zone.CONF_NAME: 'Test Zone', + zone.CONF_LATITUDE: 1.1, + zone.CONF_LONGITUDE: -2.2 + } + hass.data[zone.DOMAIN] = {} + assert await zone.async_setup_entry(hass, entry) is True + assert await zone.async_unload_entry(hass, entry) is True + assert not hass.data[zone.DOMAIN] class TestComponentZone(unittest.TestCase): @@ -20,18 +52,17 @@ class TestComponentZone(unittest.TestCase): def test_setup_no_zones_still_adds_home_zone(self): """Test if no config is passed in we still get the home zone.""" - assert setup.setup_component(self.hass, zone.DOMAIN, - {'zone': None}) - + assert setup.setup_component(self.hass, zone.DOMAIN, {'zone': None}) assert len(self.hass.states.entity_ids('zone')) == 1 state = self.hass.states.get('zone.home') assert self.hass.config.location_name == state.name assert self.hass.config.latitude == state.attributes['latitude'] assert self.hass.config.longitude == state.attributes['longitude'] assert not state.attributes.get('passive', False) + assert 'test_home' in self.hass.data[zone.DOMAIN] def test_setup(self): - """Test setup.""" + """Test a successful setup.""" info = { 'name': 'Test Zone', 'latitude': 32.880837, @@ -39,16 +70,61 @@ class TestComponentZone(unittest.TestCase): 'radius': 250, 'passive': True } - assert setup.setup_component(self.hass, zone.DOMAIN, { - 'zone': info - }) + assert setup.setup_component(self.hass, zone.DOMAIN, {'zone': info}) + assert len(self.hass.states.entity_ids('zone')) == 2 state = self.hass.states.get('zone.test_zone') assert info['name'] == state.name assert info['latitude'] == state.attributes['latitude'] assert info['longitude'] == state.attributes['longitude'] assert info['radius'] == state.attributes['radius'] assert info['passive'] == state.attributes['passive'] + assert 'test_zone' in self.hass.data[zone.DOMAIN] + assert 'test_home' in self.hass.data[zone.DOMAIN] + + def test_setup_zone_skips_home_zone(self): + """Test that zone named Home should override hass home zone.""" + info = { + 'name': 'Home', + 'latitude': 1.1, + 'longitude': -2.2, + } + assert setup.setup_component(self.hass, zone.DOMAIN, {'zone': info}) + + assert len(self.hass.states.entity_ids('zone')) == 1 + state = self.hass.states.get('zone.home') + assert info['name'] == state.name + assert 'home' in self.hass.data[zone.DOMAIN] + assert 'test_home' not in self.hass.data[zone.DOMAIN] + + def test_setup_registered_zone_skips_home_zone(self): + """Test that config entry named home should override hass home zone.""" + entry = MockConfigEntry(domain=zone.DOMAIN, data={ + zone.CONF_NAME: 'home' + }) + entry.add_to_hass(self.hass) + assert setup.setup_component(self.hass, zone.DOMAIN, {'zone': None}) + assert len(self.hass.states.entity_ids('zone')) == 0 + assert not self.hass.data[zone.DOMAIN] + + def test_setup_registered_zone_skips_configured_zone(self): + """Test if config entry will override configured zone.""" + entry = MockConfigEntry(domain=zone.DOMAIN, data={ + zone.CONF_NAME: 'Test Zone' + }) + entry.add_to_hass(self.hass) + info = { + 'name': 'Test Zone', + 'latitude': 1.1, + 'longitude': -2.2, + } + assert setup.setup_component(self.hass, zone.DOMAIN, {'zone': info}) + + assert len(self.hass.states.entity_ids('zone')) == 1 + state = self.hass.states.get('zone.test_zone') + assert not state + assert 'test_zone' not in self.hass.data[zone.DOMAIN] + assert 'test_home' in self.hass.data[zone.DOMAIN] def test_active_zone_skips_passive_zones(self): """Test active and passive zones.""" @@ -64,7 +140,7 @@ class TestComponentZone(unittest.TestCase): ] }) self.hass.block_till_done() - active = zone.active_zone(self.hass, 32.880600, -117.237561) + active = zone.zone.active_zone(self.hass, 32.880600, -117.237561) assert active is None def test_active_zone_skips_passive_zones_2(self): @@ -80,7 +156,7 @@ class TestComponentZone(unittest.TestCase): ] }) self.hass.block_till_done() - active = zone.active_zone(self.hass, 32.880700, -117.237561) + active = zone.zone.active_zone(self.hass, 32.880700, -117.237561) assert 'zone.active_zone' == active.entity_id def test_active_zone_prefers_smaller_zone_if_same_distance(self): @@ -104,7 +180,7 @@ class TestComponentZone(unittest.TestCase): ] }) - active = zone.active_zone(self.hass, latitude, longitude) + active = zone.zone.active_zone(self.hass, latitude, longitude) assert 'zone.small_zone' == active.entity_id def test_active_zone_prefers_smaller_zone_if_same_distance_2(self): @@ -122,7 +198,7 @@ class TestComponentZone(unittest.TestCase): ] }) - active = zone.active_zone(self.hass, latitude, longitude) + active = zone.zone.active_zone(self.hass, latitude, longitude) assert 'zone.smallest_zone' == active.entity_id def test_in_zone_works_for_passive_zones(self): @@ -141,5 +217,5 @@ class TestComponentZone(unittest.TestCase): ] }) - assert zone.in_zone(self.hass.states.get('zone.passive_zone'), - latitude, longitude) + assert zone.zone.in_zone(self.hass.states.get('zone.passive_zone'), + latitude, longitude)