Add ignore_attributes
option to influxdb (#37747)
* Added ignore_attributes option and tests * adjusted config for overlapping customization with ignore attrs
This commit is contained in:
parent
4ef581622d
commit
16e5d02794
3 changed files with 159 additions and 5 deletions
|
@ -51,6 +51,7 @@ from .const import (
|
||||||
CONF_DB_NAME,
|
CONF_DB_NAME,
|
||||||
CONF_DEFAULT_MEASUREMENT,
|
CONF_DEFAULT_MEASUREMENT,
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
|
CONF_IGNORE_ATTRIBUTES,
|
||||||
CONF_ORG,
|
CONF_ORG,
|
||||||
CONF_OVERRIDE_MEASUREMENT,
|
CONF_OVERRIDE_MEASUREMENT,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
|
@ -142,7 +143,10 @@ def validate_version_specific_config(conf: Dict) -> Dict:
|
||||||
|
|
||||||
|
|
||||||
_CUSTOMIZE_ENTITY_SCHEMA = vol.Schema(
|
_CUSTOMIZE_ENTITY_SCHEMA = vol.Schema(
|
||||||
{vol.Optional(CONF_OVERRIDE_MEASUREMENT): cv.string}
|
{
|
||||||
|
vol.Optional(CONF_OVERRIDE_MEASUREMENT): cv.string,
|
||||||
|
vol.Optional(CONF_IGNORE_ATTRIBUTES): vol.All(cv.ensure_list, [cv.string]),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
_INFLUX_BASE_SCHEMA = INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA.extend(
|
_INFLUX_BASE_SCHEMA = INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA.extend(
|
||||||
|
@ -154,6 +158,9 @@ _INFLUX_BASE_SCHEMA = INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA.extend(
|
||||||
vol.Optional(CONF_TAGS_ATTRIBUTES, default=[]): vol.All(
|
vol.Optional(CONF_TAGS_ATTRIBUTES, default=[]): vol.All(
|
||||||
cv.ensure_list, [cv.string]
|
cv.ensure_list, [cv.string]
|
||||||
),
|
),
|
||||||
|
vol.Optional(CONF_IGNORE_ATTRIBUTES, default=[]): vol.All(
|
||||||
|
cv.ensure_list, [cv.string]
|
||||||
|
),
|
||||||
vol.Optional(CONF_COMPONENT_CONFIG, default={}): vol.Schema(
|
vol.Optional(CONF_COMPONENT_CONFIG, default={}): vol.Schema(
|
||||||
{cv.entity_id: _CUSTOMIZE_ENTITY_SCHEMA}
|
{cv.entity_id: _CUSTOMIZE_ENTITY_SCHEMA}
|
||||||
),
|
),
|
||||||
|
@ -182,6 +189,7 @@ def _generate_event_to_json(conf: Dict) -> Callable[[Dict], str]:
|
||||||
tags_attributes = conf.get(CONF_TAGS_ATTRIBUTES)
|
tags_attributes = conf.get(CONF_TAGS_ATTRIBUTES)
|
||||||
default_measurement = conf.get(CONF_DEFAULT_MEASUREMENT)
|
default_measurement = conf.get(CONF_DEFAULT_MEASUREMENT)
|
||||||
override_measurement = conf.get(CONF_OVERRIDE_MEASUREMENT)
|
override_measurement = conf.get(CONF_OVERRIDE_MEASUREMENT)
|
||||||
|
global_ignore_attributes = set(conf[CONF_IGNORE_ATTRIBUTES])
|
||||||
component_config = EntityValues(
|
component_config = EntityValues(
|
||||||
conf[CONF_COMPONENT_CONFIG],
|
conf[CONF_COMPONENT_CONFIG],
|
||||||
conf[CONF_COMPONENT_CONFIG_DOMAIN],
|
conf[CONF_COMPONENT_CONFIG_DOMAIN],
|
||||||
|
@ -211,9 +219,8 @@ def _generate_event_to_json(conf: Dict) -> Callable[[Dict], str]:
|
||||||
_include_state = True
|
_include_state = True
|
||||||
|
|
||||||
include_uom = True
|
include_uom = True
|
||||||
measurement = component_config.get(state.entity_id).get(
|
entity_config = component_config.get(state.entity_id)
|
||||||
CONF_OVERRIDE_MEASUREMENT
|
measurement = entity_config.get(CONF_OVERRIDE_MEASUREMENT)
|
||||||
)
|
|
||||||
if measurement in (None, ""):
|
if measurement in (None, ""):
|
||||||
if override_measurement:
|
if override_measurement:
|
||||||
measurement = override_measurement
|
measurement = override_measurement
|
||||||
|
@ -241,10 +248,14 @@ def _generate_event_to_json(conf: Dict) -> Callable[[Dict], str]:
|
||||||
if _include_value:
|
if _include_value:
|
||||||
json[INFLUX_CONF_FIELDS][INFLUX_CONF_VALUE] = _state_as_value
|
json[INFLUX_CONF_FIELDS][INFLUX_CONF_VALUE] = _state_as_value
|
||||||
|
|
||||||
|
ignore_attributes = set(entity_config.get(CONF_IGNORE_ATTRIBUTES, []))
|
||||||
|
ignore_attributes.update(global_ignore_attributes)
|
||||||
for key, value in state.attributes.items():
|
for key, value in state.attributes.items():
|
||||||
if key in tags_attributes:
|
if key in tags_attributes:
|
||||||
json[INFLUX_CONF_TAGS][key] = value
|
json[INFLUX_CONF_TAGS][key] = value
|
||||||
elif key != CONF_UNIT_OF_MEASUREMENT or include_uom:
|
elif (
|
||||||
|
key != CONF_UNIT_OF_MEASUREMENT or include_uom
|
||||||
|
) and key not in ignore_attributes:
|
||||||
# If the key is already in fields
|
# If the key is already in fields
|
||||||
if key in json[INFLUX_CONF_FIELDS]:
|
if key in json[INFLUX_CONF_FIELDS]:
|
||||||
key = f"{key}_"
|
key = f"{key}_"
|
||||||
|
|
|
@ -28,6 +28,7 @@ CONF_COMPONENT_CONFIG = "component_config"
|
||||||
CONF_COMPONENT_CONFIG_GLOB = "component_config_glob"
|
CONF_COMPONENT_CONFIG_GLOB = "component_config_glob"
|
||||||
CONF_COMPONENT_CONFIG_DOMAIN = "component_config_domain"
|
CONF_COMPONENT_CONFIG_DOMAIN = "component_config_domain"
|
||||||
CONF_RETRY_COUNT = "max_retries"
|
CONF_RETRY_COUNT = "max_retries"
|
||||||
|
CONF_IGNORE_ATTRIBUTES = "ignore_attributes"
|
||||||
|
|
||||||
CONF_LANGUAGE = "language"
|
CONF_LANGUAGE = "language"
|
||||||
CONF_QUERIES = "queries"
|
CONF_QUERIES = "queries"
|
||||||
|
|
|
@ -1059,6 +1059,148 @@ async def test_event_listener_component_override_measurement(
|
||||||
write_api.reset_mock()
|
write_api.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"mock_client, config_ext, get_write_api, get_mock_call",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
influxdb.DEFAULT_API_VERSION,
|
||||||
|
BASE_V1_CONFIG,
|
||||||
|
_get_write_api_mock_v1,
|
||||||
|
influxdb.DEFAULT_API_VERSION,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
influxdb.API_VERSION_2,
|
||||||
|
BASE_V2_CONFIG,
|
||||||
|
_get_write_api_mock_v2,
|
||||||
|
influxdb.API_VERSION_2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indirect=["mock_client", "get_mock_call"],
|
||||||
|
)
|
||||||
|
async def test_event_listener_ignore_attributes(
|
||||||
|
hass, mock_client, config_ext, get_write_api, get_mock_call
|
||||||
|
):
|
||||||
|
"""Test the event listener with overridden measurements."""
|
||||||
|
config = {
|
||||||
|
"ignore_attributes": ["ignore"],
|
||||||
|
"component_config": {
|
||||||
|
"sensor.fake_humidity": {"ignore_attributes": ["id_ignore"]}
|
||||||
|
},
|
||||||
|
"component_config_glob": {
|
||||||
|
"binary_sensor.*motion": {"ignore_attributes": ["glob_ignore"]}
|
||||||
|
},
|
||||||
|
"component_config_domain": {
|
||||||
|
"climate": {"ignore_attributes": ["domain_ignore"]}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.update(config_ext)
|
||||||
|
handler_method = await _setup(hass, mock_client, config, get_write_api)
|
||||||
|
|
||||||
|
test_components = [
|
||||||
|
{
|
||||||
|
"domain": "sensor",
|
||||||
|
"id": "fake_humidity",
|
||||||
|
"attrs": {"glob_ignore": 1, "domain_ignore": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "binary_sensor",
|
||||||
|
"id": "fake_motion",
|
||||||
|
"attrs": {"id_ignore": 1, "domain_ignore": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "climate",
|
||||||
|
"id": "fake_thermostat",
|
||||||
|
"attrs": {"id_ignore": 1, "glob_ignore": 1},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
for comp in test_components:
|
||||||
|
entity_id = f"{comp['domain']}.{comp['id']}"
|
||||||
|
state = MagicMock(
|
||||||
|
state=1,
|
||||||
|
domain=comp["domain"],
|
||||||
|
entity_id=entity_id,
|
||||||
|
object_id=comp["id"],
|
||||||
|
attributes={
|
||||||
|
"ignore": 1,
|
||||||
|
"id_ignore": 1,
|
||||||
|
"glob_ignore": 1,
|
||||||
|
"domain_ignore": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
event = MagicMock(data={"new_state": state}, time_fired=12345)
|
||||||
|
fields = {"value": 1}
|
||||||
|
fields.update(comp["attrs"])
|
||||||
|
body = [
|
||||||
|
{
|
||||||
|
"measurement": entity_id,
|
||||||
|
"tags": {"domain": comp["domain"], "entity_id": comp["id"]},
|
||||||
|
"time": 12345,
|
||||||
|
"fields": fields,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
handler_method(event)
|
||||||
|
hass.data[influxdb.DOMAIN].block_till_done()
|
||||||
|
|
||||||
|
write_api = get_write_api(mock_client)
|
||||||
|
assert write_api.call_count == 1
|
||||||
|
assert write_api.call_args == get_mock_call(body)
|
||||||
|
write_api.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"mock_client, config_ext, get_write_api, get_mock_call",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
influxdb.DEFAULT_API_VERSION,
|
||||||
|
BASE_V1_CONFIG,
|
||||||
|
_get_write_api_mock_v1,
|
||||||
|
influxdb.DEFAULT_API_VERSION,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
influxdb.API_VERSION_2,
|
||||||
|
BASE_V2_CONFIG,
|
||||||
|
_get_write_api_mock_v2,
|
||||||
|
influxdb.API_VERSION_2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indirect=["mock_client", "get_mock_call"],
|
||||||
|
)
|
||||||
|
async def test_event_listener_ignore_attributes_overlapping_entities(
|
||||||
|
hass, mock_client, config_ext, get_write_api, get_mock_call
|
||||||
|
):
|
||||||
|
"""Test the event listener with overridden measurements."""
|
||||||
|
config = {
|
||||||
|
"component_config": {"sensor.fake": {"override_measurement": "units"}},
|
||||||
|
"component_config_domain": {"sensor": {"ignore_attributes": ["ignore"]}},
|
||||||
|
}
|
||||||
|
config.update(config_ext)
|
||||||
|
handler_method = await _setup(hass, mock_client, config, get_write_api)
|
||||||
|
|
||||||
|
state = MagicMock(
|
||||||
|
state=1,
|
||||||
|
domain="sensor",
|
||||||
|
entity_id="sensor.fake",
|
||||||
|
object_id="fake",
|
||||||
|
attributes={"ignore": 1},
|
||||||
|
)
|
||||||
|
event = MagicMock(data={"new_state": state}, time_fired=12345)
|
||||||
|
body = [
|
||||||
|
{
|
||||||
|
"measurement": "units",
|
||||||
|
"tags": {"domain": "sensor", "entity_id": "fake"},
|
||||||
|
"time": 12345,
|
||||||
|
"fields": {"value": 1},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
handler_method(event)
|
||||||
|
hass.data[influxdb.DOMAIN].block_till_done()
|
||||||
|
|
||||||
|
write_api = get_write_api(mock_client)
|
||||||
|
assert write_api.call_count == 1
|
||||||
|
assert write_api.call_args == get_mock_call(body)
|
||||||
|
write_api.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"mock_client, config_ext, get_write_api, get_mock_call",
|
"mock_client, config_ext, get_write_api, get_mock_call",
|
||||||
[
|
[
|
||||||
|
|
Loading…
Add table
Reference in a new issue