diff --git a/homeassistant/components/alexa/smart_home_http.py b/homeassistant/components/alexa/smart_home_http.py index df4f95f12f29e1974c6ad537b5990359a5999545..237828987e86df5f436ae6b6ce16795d2150d2a9 100644 --- a/homeassistant/components/alexa/smart_home_http.py +++ b/homeassistant/components/alexa/smart_home_http.py @@ -3,12 +3,7 @@ import logging from homeassistant import core from homeassistant.components.http.view import HomeAssistantView -from homeassistant.const import ( - CONF_CLIENT_ID, - CONF_CLIENT_SECRET, - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, -) +from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, ENTITY_CATEGORIES from homeassistant.helpers import entity_registry as er from .auth import Auth @@ -71,10 +66,7 @@ class AlexaConfig(AbstractConfig): entity_registry = er.async_get(self.hass) if registry_entry := entity_registry.async_get(entity_id): - auxiliary_entity = registry_entry.entity_category in ( - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, - ) + auxiliary_entity = registry_entry.entity_category in ENTITY_CATEGORIES else: auxiliary_entity = False return not auxiliary_entity diff --git a/homeassistant/components/cloud/alexa_config.py b/homeassistant/components/cloud/alexa_config.py index 41bab5e0bd4c6ba3d40169f34c2d7a21e1ab45fb..a6c30a5a79b332bc042be9322fce40d88b9bdd2e 100644 --- a/homeassistant/components/cloud/alexa_config.py +++ b/homeassistant/components/cloud/alexa_config.py @@ -16,11 +16,7 @@ from homeassistant.components.alexa import ( errors as alexa_errors, state_report as alexa_state_report, ) -from homeassistant.const import ( - CLOUD_NEVER_EXPOSED_ENTITIES, - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, -) +from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES, ENTITY_CATEGORIES from homeassistant.core import HomeAssistant, callback, split_entity_id from homeassistant.helpers import entity_registry as er, start from homeassistant.helpers.event import async_call_later @@ -135,10 +131,7 @@ class AlexaConfig(alexa_config.AbstractConfig): entity_registry = er.async_get(self.hass) if registry_entry := entity_registry.async_get(entity_id): - auxiliary_entity = registry_entry.entity_category in ( - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, - ) + auxiliary_entity = registry_entry.entity_category in ENTITY_CATEGORIES else: auxiliary_entity = False diff --git a/homeassistant/components/cloud/google_config.py b/homeassistant/components/cloud/google_config.py index f3f5a64bbd6df5043ed50a977f24e061daaa7b6e..9ecd76302b7abfe1d584d46e46279afb10f32c05 100644 --- a/homeassistant/components/cloud/google_config.py +++ b/homeassistant/components/cloud/google_config.py @@ -8,11 +8,7 @@ from hass_nabucasa.google_report_state import ErrorResponse from homeassistant.components.google_assistant.const import DOMAIN as GOOGLE_DOMAIN from homeassistant.components.google_assistant.helpers import AbstractConfig -from homeassistant.const import ( - CLOUD_NEVER_EXPOSED_ENTITIES, - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, -) +from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES, ENTITY_CATEGORIES from homeassistant.core import CoreState, split_entity_id from homeassistant.helpers import entity_registry as er, start from homeassistant.setup import async_setup_component @@ -133,10 +129,7 @@ class CloudGoogleConfig(AbstractConfig): entity_registry = er.async_get(self.hass) if registry_entry := entity_registry.async_get(entity_id): - auxiliary_entity = registry_entry.entity_category in ( - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, - ) + auxiliary_entity = registry_entry.entity_category in ENTITY_CATEGORIES else: auxiliary_entity = False diff --git a/homeassistant/components/energy/sensor.py b/homeassistant/components/energy/sensor.py index 8cd5702deb7b616982ec391aa8e1c520b95f0e9a..bfcc2817633291bdbd50475e7de8086c3575f01f 100644 --- a/homeassistant/components/energy/sensor.py +++ b/homeassistant/components/energy/sensor.py @@ -22,6 +22,7 @@ from homeassistant.const import ( ENERGY_KILO_WATT_HOUR, ENERGY_MEGA_WATT_HOUR, ENERGY_WATT_HOUR, + ENTITY_CATEGORY_SYSTEM, VOLUME_CUBIC_METERS, ) from homeassistant.core import ( @@ -215,6 +216,7 @@ class EnergyCostSensor(SensorEntity): utility. """ + _attr_entity_category = ENTITY_CATEGORY_SYSTEM _wrong_state_class_reported = False _wrong_unit_reported = False diff --git a/homeassistant/components/google_assistant/http.py b/homeassistant/components/google_assistant/http.py index ba7dc2597bcaf288d2d7d07210abcd98747b3560..e7a73351f6012e5698238e6874b2cd46f6ef6d7f 100644 --- a/homeassistant/components/google_assistant/http.py +++ b/homeassistant/components/google_assistant/http.py @@ -11,11 +11,7 @@ import jwt # Typing imports from homeassistant.components.http import HomeAssistantView -from homeassistant.const import ( - CLOUD_NEVER_EXPOSED_ENTITIES, - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, -) +from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES, ENTITY_CATEGORIES from homeassistant.helpers import entity_registry as er from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.util import dt as dt_util @@ -117,10 +113,7 @@ class GoogleConfig(AbstractConfig): entity_registry = er.async_get(self.hass) registry_entry = entity_registry.async_get(state.entity_id) if registry_entry: - auxiliary_entity = registry_entry.entity_category in ( - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, - ) + auxiliary_entity = registry_entry.entity_category in ENTITY_CATEGORIES else: auxiliary_entity = False diff --git a/homeassistant/components/motioneye/switch.py b/homeassistant/components/motioneye/switch.py index 9a6fe27441f301c95f929e4be94096ed3ac916c0..695cde842a797230e7ad5e8e5bd230b32af03607 100644 --- a/homeassistant/components/motioneye/switch.py +++ b/homeassistant/components/motioneye/switch.py @@ -17,8 +17,8 @@ from motioneye_client.const import ( from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ENTITY_CATEGORY_CONFIG from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.entity import ENTITY_CATEGORY_CONFIG from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import DataUpdateCoordinator diff --git a/homeassistant/const.py b/homeassistant/const.py index 587e3bb950906e5616b638fa17a29fa576180d5b..229fe704348e5d6521e178dd9089a899af3d831e 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -714,3 +714,13 @@ CAST_APP_ID_HOMEASSISTANT: Final = "B12CE3CA" ENTITY_CATEGORY_CONFIG: Final = "config" ENTITY_CATEGORY_DIAGNOSTIC: Final = "diagnostic" +ENTITY_CATEGORY_SYSTEM: Final = "system" + +# Entity categories which will: +# - Not be exposed to cloud, alexa, or google_home components +# - Not be included in indirect service calls to devices or areas +ENTITY_CATEGORIES: Final[list[str]] = [ + ENTITY_CATEGORY_CONFIG, + ENTITY_CATEGORY_DIAGNOSTIC, + ENTITY_CATEGORY_SYSTEM, +] diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index a05d2c7c2fa5be42505c7454afec54b3441f0ac7..3b2a12c687ed4e6a05ea6b657f6b5e6e75b57a59 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -26,8 +26,7 @@ from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, DEVICE_DEFAULT_NAME, - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, + ENTITY_CATEGORIES, STATE_OFF, STATE_ON, STATE_UNAVAILABLE, @@ -56,11 +55,6 @@ SOURCE_PLATFORM_CONFIG = "platform_config" FLOAT_PRECISION = abs(int(math.floor(math.log10(abs(sys.float_info.epsilon))))) - 1 -ENTITY_CATEGORIES: Final[list[str]] = [ - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, -] - ENTITY_CATEGORIES_SCHEMA: Final = vol.In(ENTITY_CATEGORIES) @@ -193,7 +187,7 @@ class EntityDescription: key: str device_class: str | None = None - entity_category: Literal["config", "diagnostic"] | None = None + entity_category: Literal["config", "diagnostic", "system"] | None = None entity_registry_enabled_default: bool = True force_update: bool = False icon: str | None = None diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index c2720c02f47421a8761fb732cffcd40c56d4fa59..00369d43536f479092530ce0cc5cbaa499b39a46 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -20,8 +20,7 @@ from homeassistant.const import ( CONF_SERVICE_DATA, CONF_SERVICE_TEMPLATE, CONF_TARGET, - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, + ENTITY_CATEGORIES, ENTITY_MATCH_ALL, ENTITY_MATCH_NONE, ) @@ -367,10 +366,7 @@ def async_extract_referenced_entity_ids( for ent_entry in ent_reg.entities.values(): # Do not add config or diagnostic entities referenced by areas or devices - if ent_entry.entity_category in ( - ENTITY_CATEGORY_CONFIG, - ENTITY_CATEGORY_DIAGNOSTIC, - ): + if ent_entry.entity_category in ENTITY_CATEGORIES: continue if ( diff --git a/tests/components/cloud/test_alexa_config.py b/tests/components/cloud/test_alexa_config.py index 9b0075db09d3a39541af037f09f00fd24b385446..8d08e924be762519ba6ffff55ab5935a51a7442e 100644 --- a/tests/components/cloud/test_alexa_config.py +++ b/tests/components/cloud/test_alexa_config.py @@ -22,19 +22,26 @@ async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs, cloud_stub): entity_registry = mock_registry(hass) entity_entry1 = entity_registry.async_get_or_create( - "switch", + "light", "test", - "switch_config_id", - suggested_object_id="config_switch", + "light_config_id", + suggested_object_id="config_light", entity_category="config", ) entity_entry2 = entity_registry.async_get_or_create( - "switch", + "light", "test", - "switch_diagnostic_id", - suggested_object_id="diagnostic_switch", + "light_diagnostic_id", + suggested_object_id="diagnostic_light", entity_category="diagnostic", ) + entity_entry3 = entity_registry.async_get_or_create( + "light", + "test", + "light_system_id", + suggested_object_id="system_light", + entity_category="system", + ) entity_conf = {"should_expose": False} await cloud_prefs.async_update( @@ -50,18 +57,21 @@ async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs, cloud_stub): assert not conf.should_expose("light.kitchen") assert not conf.should_expose(entity_entry1.entity_id) assert not conf.should_expose(entity_entry2.entity_id) + assert not conf.should_expose(entity_entry3.entity_id) entity_conf["should_expose"] = True assert conf.should_expose("light.kitchen") # config and diagnostic entities should not be exposed assert not conf.should_expose(entity_entry1.entity_id) assert not conf.should_expose(entity_entry2.entity_id) + assert not conf.should_expose(entity_entry3.entity_id) entity_conf["should_expose"] = None assert conf.should_expose("light.kitchen") # config and diagnostic entities should not be exposed assert not conf.should_expose(entity_entry1.entity_id) assert not conf.should_expose(entity_entry2.entity_id) + assert not conf.should_expose(entity_entry3.entity_id) assert "alexa" not in hass.config.components await cloud_prefs.async_update( diff --git a/tests/components/cloud/test_google_config.py b/tests/components/cloud/test_google_config.py index 99fa24a6cb976f3dbc44e5f39d4d4474949014e2..478fa22f66c1d4a6fcfb6d26dd9d99ff4b9a5c9b 100644 --- a/tests/components/cloud/test_google_config.py +++ b/tests/components/cloud/test_google_config.py @@ -223,19 +223,26 @@ async def test_google_config_expose_entity_prefs(hass, mock_conf, cloud_prefs): entity_registry = mock_registry(hass) entity_entry1 = entity_registry.async_get_or_create( - "switch", + "light", "test", - "switch_config_id", - suggested_object_id="config_switch", + "light_config_id", + suggested_object_id="config_light", entity_category="config", ) entity_entry2 = entity_registry.async_get_or_create( - "switch", + "light", "test", - "switch_diagnostic_id", - suggested_object_id="diagnostic_switch", + "light_diagnostic_id", + suggested_object_id="diagnostic_light", entity_category="diagnostic", ) + entity_entry3 = entity_registry.async_get_or_create( + "light", + "test", + "light_system_id", + suggested_object_id="system_light", + entity_category="system", + ) entity_conf = {"should_expose": False} await cloud_prefs.async_update( @@ -246,22 +253,26 @@ async def test_google_config_expose_entity_prefs(hass, mock_conf, cloud_prefs): state = State("light.kitchen", "on") state_config = State(entity_entry1.entity_id, "on") state_diagnostic = State(entity_entry2.entity_id, "on") + state_system = State(entity_entry3.entity_id, "on") assert not mock_conf.should_expose(state) assert not mock_conf.should_expose(state_config) assert not mock_conf.should_expose(state_diagnostic) + assert not mock_conf.should_expose(state_system) entity_conf["should_expose"] = True assert mock_conf.should_expose(state) # config and diagnostic entities should not be exposed assert not mock_conf.should_expose(state_config) assert not mock_conf.should_expose(state_diagnostic) + assert not mock_conf.should_expose(state_system) entity_conf["should_expose"] = None assert mock_conf.should_expose(state) # config and diagnostic entities should not be exposed assert not mock_conf.should_expose(state_config) assert not mock_conf.should_expose(state_diagnostic) + assert not mock_conf.should_expose(state_system) await cloud_prefs.async_update( google_default_expose=["sensor"], diff --git a/tests/components/google_assistant/test_google_assistant.py b/tests/components/google_assistant/test_google_assistant.py index ded7429bdee4a9ebfb42f92bcd617e747299a16c..0a916c1e18452ad87ca4cd14f6a7683696332df2 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -144,10 +144,18 @@ async def test_sync_request(hass_fixture, assistant_client, auth_header): suggested_object_id="diagnostic_switch", entity_category="diagnostic", ) + entity_entry3 = entity_registry.async_get_or_create( + "switch", + "test", + "switch_system_id", + suggested_object_id="system_switch", + entity_category="system", + ) # These should not show up in the sync request hass_fixture.states.async_set(entity_entry1.entity_id, "on") hass_fixture.states.async_set(entity_entry2.entity_id, "something_else") + hass_fixture.states.async_set(entity_entry3.entity_id, "blah") reqid = "5711642932632160983" data = {"requestId": reqid, "inputs": [{"intent": "action.devices.SYNC"}]}