diff --git a/homeassistant/config.py b/homeassistant/config.py
index 6c89c84b37b478d6de1da024d6b5cf7c5d8feb7f..f4cb1e5248b3273cf1690ee5281bf534ab255d79 100644
--- a/homeassistant/config.py
+++ b/homeassistant/config.py
@@ -13,9 +13,9 @@ import voluptuous as vol
 
 from homeassistant.const import (
     CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_PACKAGES, CONF_UNIT_SYSTEM,
-    CONF_TIME_ZONE, CONF_CUSTOMIZE, CONF_ELEVATION, CONF_UNIT_SYSTEM_METRIC,
+    CONF_TIME_ZONE, CONF_ELEVATION, CONF_UNIT_SYSTEM_METRIC,
     CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, TEMP_CELSIUS,
-    __version__)
+    __version__, CONF_CUSTOMIZE, CONF_CUSTOMIZE_DOMAIN, CONF_CUSTOMIZE_GLOB)
 from homeassistant.core import DOMAIN as CONF_CORE
 from homeassistant.exceptions import HomeAssistantError
 from homeassistant.loader import get_component
@@ -23,13 +23,14 @@ from homeassistant.util.yaml import load_yaml
 import homeassistant.helpers.config_validation as cv
 from homeassistant.util import dt as date_util, location as loc_util
 from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
-from homeassistant.helpers import customize
+from homeassistant.helpers.entity_values import EntityValues
 
 _LOGGER = logging.getLogger(__name__)
 
 YAML_CONFIG_FILE = 'configuration.yaml'
 VERSION_FILE = '.HA_VERSION'
 CONFIG_DIR_NAME = '.homeassistant'
+DATA_CUSTOMIZE = 'hass_customize'
 
 DEFAULT_CORE_CONFIG = (
     # Tuples (attribute, default, auto detect property, description)
@@ -96,7 +97,16 @@ PACKAGES_CONFIG_SCHEMA = vol.Schema({
         {cv.slug: vol.Any(dict, list)})  # Only slugs for component names
 })
 
-CORE_CONFIG_SCHEMA = vol.Schema({
+CUSTOMIZE_CONFIG_SCHEMA = vol.Schema({
+    vol.Optional(CONF_CUSTOMIZE, default={}):
+        vol.Schema({cv.entity_id: dict}),
+    vol.Optional(CONF_CUSTOMIZE_DOMAIN, default={}):
+        vol.Schema({cv.string: dict}),
+    vol.Optional(CONF_CUSTOMIZE_GLOB, default={}):
+        vol.Schema({cv.string: dict}),
+})
+
+CORE_CONFIG_SCHEMA = CUSTOMIZE_CONFIG_SCHEMA.extend({
     CONF_NAME: vol.Coerce(str),
     CONF_LATITUDE: cv.latitude,
     CONF_LONGITUDE: cv.longitude,
@@ -104,7 +114,6 @@ CORE_CONFIG_SCHEMA = vol.Schema({
     vol.Optional(CONF_TEMPERATURE_UNIT): cv.temperature_unit,
     CONF_UNIT_SYSTEM: cv.unit_system,
     CONF_TIME_ZONE: cv.time_zone,
-    vol.Optional(CONF_CUSTOMIZE, default=[]): customize.CUSTOMIZE_SCHEMA,
     vol.Optional(CONF_PACKAGES, default={}): PACKAGES_CONFIG_SCHEMA,
 })
 
@@ -289,9 +298,29 @@ def async_process_ha_core_config(hass, config):
     if CONF_TIME_ZONE in config:
         set_time_zone(config.get(CONF_TIME_ZONE))
 
-    merged_customize = merge_packages_customize(
-        config[CONF_CUSTOMIZE], config[CONF_PACKAGES])
-    customize.set_customize(hass, CONF_CORE, merged_customize)
+    # Customize
+    cust_exact = dict(config[CONF_CUSTOMIZE])
+    cust_domain = dict(config[CONF_CUSTOMIZE_DOMAIN])
+    cust_glob = dict(config[CONF_CUSTOMIZE_GLOB])
+
+    for name, pkg in config[CONF_PACKAGES].items():
+        pkg_cust = pkg.get(CONF_CORE)
+
+        if pkg_cust is None:
+            continue
+
+        try:
+            pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust)
+        except vol.Invalid:
+            _LOGGER.warning('Package %s contains invalid customize', name)
+            continue
+
+        cust_exact.update(pkg_cust[CONF_CUSTOMIZE])
+        cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN])
+        cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB])
+
+    hass.data[DATA_CUSTOMIZE] = \
+        EntityValues(cust_exact, cust_domain, cust_glob)
 
     if CONF_UNIT_SYSTEM in config:
         if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
@@ -446,20 +475,6 @@ def merge_packages_config(config, packages):
     return config
 
 
-def merge_packages_customize(core_customize, packages):
-    """Merge customize from packages."""
-    schema = vol.Schema({
-        vol.Optional(CONF_CORE): vol.Schema({
-            CONF_CUSTOMIZE: customize.CUSTOMIZE_SCHEMA}),
-    }, extra=vol.ALLOW_EXTRA)
-
-    cust = list(core_customize)
-    for pkg in packages.values():
-        conf = schema(pkg)
-        cust.extend(conf.get(CONF_CORE, {}).get(CONF_CUSTOMIZE, []))
-    return cust
-
-
 @asyncio.coroutine
 def async_check_ha_config_file(hass):
     """Check if HA config file valid.
diff --git a/homeassistant/const.py b/homeassistant/const.py
index f5f86321b1e5cf5850991e35153958161fbd5163..3de056753e6bdb87ee2ad12ad43fa955d17e9158 100644
--- a/homeassistant/const.py
+++ b/homeassistant/const.py
@@ -77,6 +77,8 @@ CONF_COMMAND_STOP = 'command_stop'
 CONF_CONDITION = 'condition'
 CONF_COVERS = 'covers'
 CONF_CUSTOMIZE = 'customize'
+CONF_CUSTOMIZE_DOMAIN = 'customize_domain'
+CONF_CUSTOMIZE_GLOB = 'customize_glob'
 CONF_DEVICE = 'device'
 CONF_DEVICE_CLASS = 'device_class'
 CONF_DEVICES = 'devices'
diff --git a/homeassistant/helpers/customize.py b/homeassistant/helpers/customize.py
deleted file mode 100644
index e9cd7c0269ae7f2a2c68b5b2948c2e583c26e097..0000000000000000000000000000000000000000
--- a/homeassistant/helpers/customize.py
+++ /dev/null
@@ -1,107 +0,0 @@
-"""A helper module for customization."""
-import collections
-from typing import Any, Dict, List
-import fnmatch
-import voluptuous as vol
-
-from homeassistant.const import CONF_ENTITY_ID
-from homeassistant.core import HomeAssistant, split_entity_id
-import homeassistant.helpers.config_validation as cv
-
-_OVERWRITE_KEY_FORMAT = '{}.overwrite'
-_OVERWRITE_CACHE_KEY_FORMAT = '{}.overwrite_cache'
-
-_CUSTOMIZE_SCHEMA_ENTRY = vol.Schema({
-    vol.Required(CONF_ENTITY_ID): vol.All(
-        cv.ensure_list_csv, vol.Length(min=1), [vol.Schema(str)], [vol.Lower])
-}, extra=vol.ALLOW_EXTRA)
-
-
-def _convert_old_config(inp: Any) -> List:
-    if not isinstance(inp, dict):
-        return cv.ensure_list(inp)
-    if CONF_ENTITY_ID in inp:
-        return [inp]  # sigle entry
-    res = []
-
-    inp = vol.Schema({cv.match_all: dict})(inp)
-    for key, val in inp.items():
-        val = dict(val)
-        val[CONF_ENTITY_ID] = key
-        res.append(val)
-    return res
-
-
-CUSTOMIZE_SCHEMA = vol.All(_convert_old_config, [_CUSTOMIZE_SCHEMA_ENTRY])
-
-
-def set_customize(
-        hass: HomeAssistant, domain: str, customize: List[Dict]) -> None:
-    """Overwrite all current customize settings.
-
-    Async friendly.
-    """
-    hass.data[_OVERWRITE_KEY_FORMAT.format(domain)] = customize
-    hass.data[_OVERWRITE_CACHE_KEY_FORMAT.format(domain)] = {}
-
-
-def get_overrides(hass: HomeAssistant, domain: str, entity_id: str) -> Dict:
-    """Return a dictionary of overrides related to entity_id.
-
-    Whole-domain overrides are of lowest priorities,
-    then glob on entity ID, and finally exact entity_id
-    matches are of highest priority.
-
-    The lookups are cached.
-    """
-    cache_key = _OVERWRITE_CACHE_KEY_FORMAT.format(domain)
-    if cache_key in hass.data and entity_id in hass.data[cache_key]:
-        return hass.data[cache_key][entity_id]
-    overwrite_key = _OVERWRITE_KEY_FORMAT.format(domain)
-    if overwrite_key not in hass.data:
-        return {}
-    domain_result = {}  # type: Dict[str, Any]
-    glob_result = {}  # type: Dict[str, Any]
-    exact_result = {}  # type: Dict[str, Any]
-    domain = split_entity_id(entity_id)[0]
-
-    def clean_entry(entry: Dict) -> Dict:
-        """Clean up entity-matching keys."""
-        entry.pop(CONF_ENTITY_ID, None)
-        return entry
-
-    def deep_update(target: Dict, source: Dict) -> None:
-        """Deep update a dictionary."""
-        for key, value in source.items():
-            if isinstance(value, collections.Mapping):
-                updated_value = target.get(key, {})
-                # If the new value is map, but the old value is not -
-                # overwrite the old value.
-                if not isinstance(updated_value, collections.Mapping):
-                    updated_value = {}
-                deep_update(updated_value, value)
-                target[key] = updated_value
-            else:
-                target[key] = source[key]
-
-    for rule in hass.data[overwrite_key]:
-        if CONF_ENTITY_ID in rule:
-            entities = rule[CONF_ENTITY_ID]
-            if domain in entities:
-                deep_update(domain_result, rule)
-            if entity_id in entities:
-                deep_update(exact_result, rule)
-            for entity_id_glob in entities:
-                if entity_id_glob == entity_id:
-                    continue
-                if fnmatch.fnmatchcase(entity_id, entity_id_glob):
-                    deep_update(glob_result, rule)
-                    break
-    result = {}
-    deep_update(result, clean_entry(domain_result))
-    deep_update(result, clean_entry(glob_result))
-    deep_update(result, clean_entry(exact_result))
-    if cache_key not in hass.data:
-        hass.data[cache_key] = {}
-    hass.data[cache_key][entity_id] = result
-    return result
diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py
index e66f49cc6dcede8ef7c5e1323945063eb4b92b2b..0705f60a9b69d923e8c27e9e1af044a99c1a82d5 100644
--- a/homeassistant/helpers/entity.py
+++ b/homeassistant/helpers/entity.py
@@ -11,12 +11,12 @@ from homeassistant.const import (
     ATTR_UNIT_OF_MEASUREMENT, DEVICE_DEFAULT_NAME, STATE_OFF, STATE_ON,
     STATE_UNAVAILABLE, STATE_UNKNOWN, TEMP_CELSIUS, TEMP_FAHRENHEIT,
     ATTR_ENTITY_PICTURE, ATTR_SUPPORTED_FEATURES, ATTR_DEVICE_CLASS)
-from homeassistant.core import HomeAssistant, DOMAIN as CORE_DOMAIN
+from homeassistant.core import HomeAssistant
+from homeassistant.config import DATA_CUSTOMIZE
 from homeassistant.exceptions import NoEntitySpecifiedError
 from homeassistant.util import ensure_unique_string, slugify
 from homeassistant.util.async import (
     run_coroutine_threadsafe, run_callback_threadsafe)
-from homeassistant.helpers.customize import get_overrides
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -209,8 +209,6 @@ class Entity(object):
                 # pylint: disable=no-member
                 yield from self.async_update()
             else:
-                # PS: Run this in our own thread pool once we have
-                #     future support?
                 yield from self.hass.loop.run_in_executor(None, self.update)
 
         start = timer()
@@ -253,7 +251,8 @@ class Entity(object):
                             end - start)
 
         # Overwrite properties that have been set in the config file.
-        attr.update(get_overrides(self.hass, CORE_DOMAIN, self.entity_id))
+        if DATA_CUSTOMIZE in self.hass.data:
+            attr.update(self.hass.data[DATA_CUSTOMIZE].get(self.entity_id))
 
         # Remove hidden property if false so it won't show up.
         if not attr.get(ATTR_HIDDEN, True):
diff --git a/homeassistant/helpers/entity_values.py b/homeassistant/helpers/entity_values.py
new file mode 100644
index 0000000000000000000000000000000000000000..19980394d26d72792541876a6d7d38caad1d4fa6
--- /dev/null
+++ b/homeassistant/helpers/entity_values.py
@@ -0,0 +1,46 @@
+"""A class to hold entity values."""
+from collections import OrderedDict
+import fnmatch
+import re
+
+from homeassistant.core import split_entity_id
+
+
+class EntityValues(object):
+    """Class to store entity id based values."""
+
+    def __init__(self, exact=None, domain=None, glob=None):
+        """Initialize an EntityConfigDict."""
+        self._cache = {}
+        self._exact = exact
+        self._domain = domain
+
+        if glob is None:
+            compiled = None
+        else:
+            compiled = OrderedDict()
+            for key, value in glob.items():
+                compiled[re.compile(fnmatch.translate(key))] = value
+
+        self._glob = compiled
+
+    def get(self, entity_id):
+        """Get config for an entity id."""
+        if entity_id in self._cache:
+            return self._cache[entity_id]
+
+        domain, _ = split_entity_id(entity_id)
+        result = self._cache[entity_id] = {}
+
+        if self._domain is not None and domain in self._domain:
+            result.update(self._domain[domain])
+
+        if self._glob is not None:
+            for pattern, values in self._glob.items():
+                if pattern.match(entity_id):
+                    result.update(values)
+
+        if self._exact is not None and entity_id in self._exact:
+            result.update(self._exact[entity_id])
+
+        return result
diff --git a/tests/helpers/test_customize.py b/tests/helpers/test_customize.py
deleted file mode 100644
index 0fb1b6ab14c4a3a9a423eb310ab73982ef01f0a1..0000000000000000000000000000000000000000
--- a/tests/helpers/test_customize.py
+++ /dev/null
@@ -1,119 +0,0 @@
-"""Test the customize helper."""
-import homeassistant.helpers.customize as customize
-from voluptuous import MultipleInvalid
-import pytest
-
-
-class MockHass(object):
-    """Mock object for HassAssistant."""
-
-    data = {}
-
-
-class TestHelpersCustomize(object):
-    """Test homeassistant.helpers.customize module."""
-
-    def setup_method(self, method):
-        """Setup things to be run when tests are started."""
-        self.entity_id = 'test.test'
-        self.hass = MockHass()
-
-    def _get_overrides(self, overrides):
-        test_domain = 'test.domain'
-        customize.set_customize(self.hass, test_domain, overrides)
-        return customize.get_overrides(self.hass, test_domain, self.entity_id)
-
-    def test_override_single_value(self):
-        """Test entity customization through configuration."""
-        result = self._get_overrides([
-            {'entity_id': [self.entity_id], 'key': 'value'}])
-
-        assert result == {'key': 'value'}
-
-    def test_override_multiple_values(self):
-        """Test entity customization through configuration."""
-        result = self._get_overrides([
-            {'entity_id': [self.entity_id], 'key1': 'value1'},
-            {'entity_id': [self.entity_id], 'key2': 'value2'}])
-
-        assert result == {'key1': 'value1', 'key2': 'value2'}
-
-    def test_override_same_value(self):
-        """Test entity customization through configuration."""
-        result = self._get_overrides([
-            {'entity_id': [self.entity_id], 'key': 'value1'},
-            {'entity_id': [self.entity_id], 'key': 'value2'}])
-
-        assert result == {'key': 'value2'}
-
-    def test_override_by_domain(self):
-        """Test entity customization through configuration."""
-        result = self._get_overrides([
-            {'entity_id': ['test'], 'key': 'value'}])
-
-        assert result == {'key': 'value'}
-
-    def test_override_by_glob(self):
-        """Test entity customization through configuration."""
-        result = self._get_overrides([
-            {'entity_id': ['test.?e*'], 'key': 'value'}])
-
-        assert result == {'key': 'value'}
-
-    def test_override_exact_over_glob_over_domain(self):
-        """Test entity customization through configuration."""
-        result = self._get_overrides([
-            {'entity_id': ['test.test'], 'key1': 'valueExact'},
-            {'entity_id': ['test.tes?'],
-             'key1': 'valueGlob',
-             'key2': 'valueGlob'},
-            {'entity_id': ['test'],
-             'key1': 'valueDomain',
-             'key2': 'valueDomain',
-             'key3': 'valueDomain'}])
-
-        assert result == {
-            'key1': 'valueExact',
-            'key2': 'valueGlob',
-            'key3': 'valueDomain'}
-
-    def test_override_deep_dict(self):
-        """Test we can deep-overwrite a dict."""
-        result = self._get_overrides(
-            [{'entity_id': [self.entity_id],
-              'test': {'key1': 'value1', 'key2': 'value2'}},
-             {'entity_id': [self.entity_id],
-              'test': {'key3': 'value3', 'key2': 'value22'}}])
-        assert result['test'] == {
-            'key1': 'value1',
-            'key2': 'value22',
-            'key3': 'value3'}
-
-    def test_schema_bad_schema(self):
-        """Test bad customize schemas."""
-        for value in (
-                {'test.test': 10},
-                {'test.test': ['hello']},
-                {'entity_id': {'a': 'b'}},
-                {'entity_id': 10},
-                [{'test.test': 'value'}],
-        ):
-            with pytest.raises(
-                MultipleInvalid,
-                message="{} should have raised MultipleInvalid".format(
-                    value)):
-                customize.CUSTOMIZE_SCHEMA(value)
-
-    def test_get_customize_schema_allow_extra(self):
-        """Test schema with ALLOW_EXTRA."""
-        for value in (
-                {'test.test': {'hidden': True}},
-                {'test.test': {'key': ['value1', 'value2']}},
-                [{'entity_id': 'id1', 'key': 'value'}],
-        ):
-            customize.CUSTOMIZE_SCHEMA(value)
-
-    def test_get_customize_schema_csv(self):
-        """Test schema with comma separated entity IDs."""
-        assert [{'entity_id': ['id1', 'id2', 'id3']}] == \
-            customize.CUSTOMIZE_SCHEMA([{'entity_id': 'id1,ID2 , id3'}])
diff --git a/tests/helpers/test_entity.py b/tests/helpers/test_entity.py
index c114478319fb7172607c19481bd94f1fbd98728d..965afde83092bb5e75cf3ec39965790bc04ec4d8 100644
--- a/tests/helpers/test_entity.py
+++ b/tests/helpers/test_entity.py
@@ -7,8 +7,9 @@ from unittest.mock import patch
 import pytest
 
 import homeassistant.helpers.entity as entity
-from homeassistant.helpers.customize import set_customize
 from homeassistant.const import ATTR_HIDDEN, ATTR_DEVICE_CLASS
+from homeassistant.config import DATA_CUSTOMIZE
+from homeassistant.helpers.entity_values import EntityValues
 
 from tests.common import get_test_home_assistant
 
@@ -89,10 +90,8 @@ class TestHelpersEntity(object):
 
     def test_overwriting_hidden_property_to_true(self):
         """Test we can overwrite hidden property to True."""
-        set_customize(
-            self.hass,
-            entity.CORE_DOMAIN,
-            [{'entity_id': [self.entity.entity_id], ATTR_HIDDEN: True}])
+        self.hass.data[DATA_CUSTOMIZE] = EntityValues({
+            self.entity.entity_id: {ATTR_HIDDEN: True}})
         self.entity.update_ha_state()
 
         state = self.hass.states.get(self.entity.entity_id)
diff --git a/tests/helpers/test_entity_values.py b/tests/helpers/test_entity_values.py
new file mode 100644
index 0000000000000000000000000000000000000000..332591165b5072cfff522f36100b62b1d443db9b
--- /dev/null
+++ b/tests/helpers/test_entity_values.py
@@ -0,0 +1,68 @@
+"""Test the entity values helper."""
+from collections import OrderedDict
+from homeassistant.helpers.entity_values import EntityValues as EV
+
+ent = 'test.test'
+
+
+def test_override_single_value():
+    """Test values with exact match."""
+    store = EV({ent: {'key': 'value'}})
+    assert store.get(ent) == {'key': 'value'}
+    assert len(store._cache) == 1
+    assert store.get(ent) == {'key': 'value'}
+    assert len(store._cache) == 1
+
+
+def test_override_by_domain():
+    """Test values with domain match."""
+    store = EV(domain={'test': {'key': 'value'}})
+    assert store.get(ent) == {'key': 'value'}
+
+
+def test_override_by_glob():
+    """Test values with glob match."""
+    store = EV(glob={'test.?e*': {'key': 'value'}})
+    assert store.get(ent) == {'key': 'value'}
+
+
+def test_glob_overrules_domain():
+    """Test domain overrules glob match."""
+    store = EV(
+        domain={'test': {'key': 'domain'}},
+        glob={'test.?e*': {'key': 'glob'}})
+    assert store.get(ent) == {'key': 'glob'}
+
+
+def test_exact_overrules_domain():
+    """Test exact overrules domain match."""
+    store = EV(
+        exact={'test.test': {'key': 'exact'}},
+        domain={'test': {'key': 'domain'}},
+        glob={'test.?e*': {'key': 'glob'}})
+    assert store.get(ent) == {'key': 'exact'}
+
+
+def test_merging_values():
+    """Test merging glob, domain and exact configs."""
+    store = EV(
+        exact={'test.test': {'exact_key': 'exact'}},
+        domain={'test': {'domain_key': 'domain'}},
+        glob={'test.?e*': {'glob_key': 'glob'}})
+    assert store.get(ent) == {
+        'exact_key': 'exact',
+        'domain_key': 'domain',
+        'glob_key': 'glob',
+    }
+
+
+def test_glob_order():
+    """Test merging glob, domain and exact configs."""
+    glob = OrderedDict()
+    glob['test.*est'] = {"value": "first"}
+    glob['test.*'] = {"value": "second"}
+
+    store = EV(glob=glob)
+    assert store.get(ent) == {
+        'value': 'second'
+    }
diff --git a/tests/test_config.py b/tests/test_config.py
index a796cc615efd7989a71c2502b7700029b7a72272..fefc39544e3db5c433060080b0889f0f22319b85 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -547,11 +547,5 @@ def test_merge_customize(hass):
     }
     yield from config_util.async_process_ha_core_config(hass, core_config)
 
-    entity = Entity()
-    entity.entity_id = 'b.b'
-    entity.hass = hass
-    yield from entity.async_update_ha_state()
-
-    state = hass.states.get('b.b')
-    assert state is not None
-    assert state.attributes['friendly_name'] == 'BB'
+    assert hass.data[config_util.DATA_CUSTOMIZE].get('b.b') == \
+        {'friendly_name': 'BB'}