diff --git a/custom_components/elasticsearch/config_flow.py b/custom_components/elasticsearch/config_flow.py index 05fd701..521f231 100644 --- a/custom_components/elasticsearch/config_flow.py +++ b/custom_components/elasticsearch/config_flow.py @@ -31,6 +31,7 @@ from custom_components.elasticsearch.const import ( CONF_AUTHENTICATION_TYPE, CONF_CHANGE_DETECTION_TYPE, + CONF_FILTER_OUT_ZERO_FLOAT, CONF_EXCLUDE_TARGETS, CONF_INCLUDE_TARGETS, CONF_POLLING_FREQUENCY, @@ -361,6 +362,7 @@ class ElasticOptionsFlowHandler(config_entries.OptionsFlow): CONF_PUBLISH_FREQUENCY: ONE_MINUTE, CONF_POLLING_FREQUENCY: ONE_MINUTE, CONF_CHANGE_DETECTION_TYPE: [StateChangeType.STATE.value, StateChangeType.ATTRIBUTE.value], + CONF_FILTER_OUT_ZERO_FLOAT: False, CONF_INCLUDE_TARGETS: False, CONF_EXCLUDE_TARGETS: False, CONF_TARGETS_TO_INCLUDE: {}, @@ -419,6 +421,10 @@ def _build_options_schema(self) -> vol.Schema: "schema": CONF_TAGS, "default": from_options(CONF_TAGS), } + SCHEMA_FILTER_OUT_ZERO_FLOAT = { + "schema": CONF_FILTER_OUT_ZERO_FLOAT, + "default": from_options(CONF_FILTER_OUT_ZERO_FLOAT), + } SCHEMA_INCLUDE_TARGETS = { "schema": CONF_INCLUDE_TARGETS, "default": from_options(CONF_INCLUDE_TARGETS), @@ -465,6 +471,9 @@ def _build_options_schema(self) -> vol.Schema: vol.Optional(**SCHEMA_TAGS): SelectSelector( SelectSelectorConfig(options=[], custom_value=True, multiple=True) ), + vol.Optional(**SCHEMA_FILTER_OUT_ZERO_FLOAT): BooleanSelector( + BooleanSelectorConfig(), + ), vol.Optional(**SCHEMA_INCLUDE_TARGETS): BooleanSelector( BooleanSelectorConfig(), ), diff --git a/custom_components/elasticsearch/const.py b/custom_components/elasticsearch/const.py index 14e4903..569e695 100644 --- a/custom_components/elasticsearch/const.py +++ b/custom_components/elasticsearch/const.py @@ -17,6 +17,8 @@ CONF_DEBUG_ATTRIBUTE_FILTERING: str = "debug_attribute_filtering" +CONF_FILTER_OUT_ZERO_FLOAT = "filter_out_zero_float" + CONF_INCLUDE_TARGETS: str = "include_targets" CONF_EXCLUDE_TARGETS: str = "exclude_targets" diff --git a/custom_components/elasticsearch/es_integration.py b/custom_components/elasticsearch/es_integration.py index 2968f86..a9f24cc 100644 --- a/custom_components/elasticsearch/es_integration.py +++ b/custom_components/elasticsearch/es_integration.py @@ -17,6 +17,7 @@ from custom_components.elasticsearch.const import ( CONF_CHANGE_DETECTION_TYPE, CONF_DEBUG_ATTRIBUTE_FILTERING, + CONF_FILTER_OUT_ZERO_FLOAT, CONF_EXCLUDE_TARGETS, CONF_INCLUDE_TARGETS, CONF_POLLING_FREQUENCY, @@ -125,6 +126,7 @@ def build_pipeline_manager_parameters(cls, hass, gateway, config_entry: ConfigEn change_detection_type=config_entry.options[CONF_CHANGE_DETECTION_TYPE], tags=config_entry.options[CONF_TAGS], debug_attribute_filtering=config_entry.options.get(CONF_DEBUG_ATTRIBUTE_FILTERING, False), + filter_out_zero_float=config_entry.options.get(CONF_FILTER_OUT_ZERO_FLOAT, False), include_targets=config_entry.options[CONF_INCLUDE_TARGETS], exclude_targets=config_entry.options[CONF_EXCLUDE_TARGETS], included_areas=config_entry.options[CONF_TARGETS_TO_INCLUDE].get("area_id", []), diff --git a/custom_components/elasticsearch/es_publish_pipeline.py b/custom_components/elasticsearch/es_publish_pipeline.py index dd7039b..a9339b0 100644 --- a/custom_components/elasticsearch/es_publish_pipeline.py +++ b/custom_components/elasticsearch/es_publish_pipeline.py @@ -87,6 +87,7 @@ def __init__( include_targets: bool, exclude_targets: bool, debug_attribute_filtering: bool, + filter_out_zero_float: bool, included_areas: list[str], excluded_areas: list[str], included_labels: list[str], @@ -106,6 +107,7 @@ def __init__( self.change_detection_type: list[StateChangeType] = change_detection_type self.tags: list[str] = tags self.debug_attribute_filtering: bool = debug_attribute_filtering + self.filter_out_zero_float: bool = filter_out_zero_float self.include_targets: bool = include_targets self.exclude_targets: bool = exclude_targets self.included_labels: list[str] = included_labels @@ -281,7 +283,7 @@ def __init__( ) -> None: """Initialize the filterer.""" self._logger = log if log else BASE_LOGGER - + self._filter_out_zero_float: bool = settings.filter_out_zero_float self._include_targets: bool = settings.include_targets self._exclude_targets: bool = settings.exclude_targets @@ -325,6 +327,18 @@ def passes_filter(self, state: State, reason: StateChangeType) -> bool: if not self._passes_change_detection_type_filter(reason): return False + if self._filter_out_zero_float: + succeeded_as_boolean, _ = Pipeline.Formatter.try_state_as_boolean(state) + if not succeeded_as_boolean: + # If not a boolean, try as number + succeeded_as_number, numeric_value = Pipeline.Formatter.try_state_as_number(state) + if succeeded_as_number and numeric_value == 0.0: + # This state would result in "hass.entity.valueas": {"float": 0.0} in Elasticsearch. + return self._reject( + base_msg, + "State value 0.0 is ignored according to configuration." + ) + entity: RegistryEntry | None = self._entity_registry.async_get(state.entity_id) if not entity: diff --git a/custom_components/elasticsearch/translations/en.json b/custom_components/elasticsearch/translations/en.json index e1016d8..5715751 100644 --- a/custom_components/elasticsearch/translations/en.json +++ b/custom_components/elasticsearch/translations/en.json @@ -70,6 +70,7 @@ "polling_frequency": "Gather all entity states at this interval", "change_detection_type": "Choose what types of entity changes to listen for and publish", "tags": "Tags to apply to all published events", + "filter_out_zero_float": "Exlude events where numerical value is 0", "include_targets": "Toggle to only publish the set of targets below", "exclude_targets": "Toggle to exclude publishing the set of targets below", "targets_to_include": "Select the targets to include",