예제 #1
0
파일: validate.py 프로젝트: jbouwh/core
def _async_validate_usage_stat(
    hass: HomeAssistant,
    metadata: dict[str, tuple[int, recorder.models.StatisticMetaData]],
    stat_id: str,
    allowed_device_classes: Sequence[str],
    allowed_units: Mapping[str, Sequence[str]],
    unit_error: str,
    result: list[ValidationIssue],
) -> None:
    """Validate a statistic."""
    if stat_id not in metadata:
        result.append(ValidationIssue("statistics_not_defined", stat_id))

    has_entity_source = valid_entity_id(stat_id)

    if not has_entity_source:
        return

    entity_id = stat_id

    if not recorder.is_entity_recorded(hass, entity_id):
        result.append(ValidationIssue(
            "recorder_untracked",
            entity_id,
        ))
        return

    if (state := hass.states.get(entity_id)) is None:
        result.append(ValidationIssue(
            "entity_not_defined",
            entity_id,
        ))
        return
예제 #2
0
def _async_validate_cost_entity(hass: HomeAssistant, entity_id: str,
                                result: list[ValidationIssue]) -> None:
    """Validate that the cost entity is correct."""
    if not recorder.is_entity_recorded(hass, entity_id):
        result.append(ValidationIssue(
            "recorder_untracked",
            entity_id,
        ))

    state = hass.states.get(entity_id)

    if state is None:
        result.append(ValidationIssue(
            "entity_not_defined",
            entity_id,
        ))
        return

    state_class = state.attributes.get("state_class")

    supported_state_classes = [
        sensor.STATE_CLASS_MEASUREMENT,
        sensor.STATE_CLASS_TOTAL_INCREASING,
    ]
    if state_class not in supported_state_classes:
        result.append(
            ValidationIssue("entity_unexpected_state_class_total_increasing",
                            entity_id, state_class))
예제 #3
0
def _async_validate_auto_generated_cost_entity(
        hass: HomeAssistant, entity_id: str,
        result: list[ValidationIssue]) -> None:
    """Validate that the auto generated cost entity is correct."""
    if not recorder.is_entity_recorded(hass, entity_id):
        result.append(ValidationIssue(
            "recorder_untracked",
            entity_id,
        ))
예제 #4
0
파일: validate.py 프로젝트: jbouwh/core
def _async_validate_auto_generated_cost_entity(
        hass: HomeAssistant, energy_entity_id: str,
        result: list[ValidationIssue]) -> None:
    """Validate that the auto generated cost entity is correct."""
    if energy_entity_id not in hass.data[DOMAIN]["cost_sensors"]:
        # The cost entity has not been setup
        return

    cost_entity_id = hass.data[DOMAIN]["cost_sensors"][energy_entity_id]
    if not recorder.is_entity_recorded(hass, cost_entity_id):
        result.append(ValidationIssue("recorder_untracked", cost_entity_id))
예제 #5
0
def _get_sensor_states(hass: HomeAssistant) -> list[State]:
    """Get the current state of all sensors for which to compile statistics."""
    all_sensors = hass.states.all(DOMAIN)
    statistics_sensors = []

    for state in all_sensors:
        if not is_entity_recorded(hass, state.entity_id):
            continue
        if (state.attributes.get(ATTR_STATE_CLASS)) not in STATE_CLASSES:
            continue
        statistics_sensors.append(state)

    return statistics_sensors
예제 #6
0
def _async_validate_cost_stat(hass: HomeAssistant, stat_id: str,
                              result: list[ValidationIssue]) -> None:
    """Validate that the cost stat is correct."""
    has_entity = valid_entity_id(stat_id)

    if not has_entity:
        return

    if not recorder.is_entity_recorded(hass, stat_id):
        result.append(ValidationIssue(
            "recorder_untracked",
            stat_id,
        ))
예제 #7
0
파일: validate.py 프로젝트: jbouwh/core
def _async_validate_cost_stat(
    hass: HomeAssistant,
    metadata: dict[str, tuple[int, recorder.models.StatisticMetaData]],
    stat_id: str,
    result: list[ValidationIssue],
) -> None:
    """Validate that the cost stat is correct."""
    if stat_id not in metadata:
        result.append(ValidationIssue("statistics_not_defined", stat_id))

    has_entity = valid_entity_id(stat_id)

    if not has_entity:
        return

    if not recorder.is_entity_recorded(hass, stat_id):
        result.append(ValidationIssue("recorder_untracked", stat_id))

    if (state := hass.states.get(stat_id)) is None:
        result.append(ValidationIssue("entity_not_defined", stat_id))
        return
예제 #8
0
def _async_validate_cost_stat(hass: HomeAssistant, stat_id: str,
                              result: list[ValidationIssue]) -> None:
    """Validate that the cost stat is correct."""
    has_entity = valid_entity_id(stat_id)

    if not has_entity:
        return

    if not recorder.is_entity_recorded(hass, stat_id):
        result.append(ValidationIssue(
            "recorder_untracked",
            stat_id,
        ))

    state = hass.states.get(stat_id)

    if state is None:
        result.append(ValidationIssue(
            "entity_not_defined",
            stat_id,
        ))
        return

    state_class = state.attributes.get("state_class")

    supported_state_classes = [
        sensor.STATE_CLASS_MEASUREMENT,
        sensor.STATE_CLASS_TOTAL,
        sensor.STATE_CLASS_TOTAL_INCREASING,
    ]
    if state_class not in supported_state_classes:
        result.append(
            ValidationIssue("entity_unexpected_state_class", stat_id,
                            state_class))

    if (state_class == sensor.STATE_CLASS_MEASUREMENT
            and sensor.ATTR_LAST_RESET not in state.attributes):
        result.append(
            ValidationIssue("entity_state_class_measurement_no_last_reset",
                            stat_id))
예제 #9
0
async def _async_validate_usage_stat(
    hass: HomeAssistant,
    stat_id: str,
    allowed_device_classes: Sequence[str],
    allowed_units: Mapping[str, Sequence[str]],
    unit_error: str,
    result: list[ValidationIssue],
) -> None:
    """Validate a statistic."""
    metadata = await hass.async_add_executor_job(
        functools.partial(
            recorder.statistics.get_metadata,
            hass,
            statistic_ids=(stat_id, ),
        ))

    if stat_id not in metadata:
        result.append(ValidationIssue("statistics_not_defined", stat_id))

    has_entity_source = valid_entity_id(stat_id)

    if not has_entity_source:
        return

    entity_id = stat_id

    if not recorder.is_entity_recorded(hass, entity_id):
        result.append(ValidationIssue(
            "recorder_untracked",
            entity_id,
        ))
        return

    if (state := hass.states.get(entity_id)) is None:
        result.append(ValidationIssue(
            "entity_not_defined",
            entity_id,
        ))
        return
예제 #10
0
async def _async_validate_cost_stat(hass: HomeAssistant, stat_id: str,
                                    result: list[ValidationIssue]) -> None:
    """Validate that the cost stat is correct."""
    metadata = await hass.async_add_executor_job(
        functools.partial(
            recorder.statistics.get_metadata,
            hass,
            statistic_ids=(stat_id, ),
        ))

    if stat_id not in metadata:
        result.append(ValidationIssue("statistics_not_defined", stat_id))

    has_entity = valid_entity_id(stat_id)

    if not has_entity:
        return

    if not recorder.is_entity_recorded(hass, stat_id):
        result.append(ValidationIssue("recorder_untracked", stat_id))

    if (state := hass.states.get(stat_id)) is None:
        result.append(ValidationIssue("entity_not_defined", stat_id))
        return
예제 #11
0
def validate_statistics(
    hass: HomeAssistant, ) -> dict[str, list[statistics.ValidationIssue]]:
    """Validate statistics."""
    validation_result = defaultdict(list)

    sensor_states = hass.states.all(DOMAIN)
    metadatas = statistics.get_metadata(hass, statistic_source=RECORDER_DOMAIN)
    sensor_entity_ids = {i.entity_id for i in sensor_states}
    sensor_statistic_ids = set(metadatas)

    for state in sensor_states:
        entity_id = state.entity_id
        device_class = state.attributes.get(ATTR_DEVICE_CLASS)
        state_class = state.attributes.get(ATTR_STATE_CLASS)
        state_unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)

        if metadata := metadatas.get(entity_id):
            if not is_entity_recorded(hass, state.entity_id):
                # Sensor was previously recorded, but no longer is
                validation_result[entity_id].append(
                    statistics.ValidationIssue(
                        "entity_no_longer_recorded",
                        {"statistic_id": entity_id},
                    ))

            if state_class not in STATE_CLASSES:
                # Sensor no longer has a valid state class
                validation_result[entity_id].append(
                    statistics.ValidationIssue(
                        "unsupported_state_class",
                        {
                            "statistic_id": entity_id,
                            "state_class": state_class
                        },
                    ))

            metadata_unit = metadata[1]["unit_of_measurement"]
            if device_class not in UNIT_CONVERSIONS:
                if state_unit != metadata_unit:
                    # The unit has changed
                    validation_result[entity_id].append(
                        statistics.ValidationIssue(
                            "units_changed",
                            {
                                "statistic_id": entity_id,
                                "state_unit": state_unit,
                                "metadata_unit": metadata_unit,
                            },
                        ))
            elif metadata_unit != DEVICE_CLASS_UNITS[device_class]:
                # The unit in metadata is not supported for this device class
                validation_result[entity_id].append(
                    statistics.ValidationIssue(
                        "unsupported_unit_metadata",
                        {
                            "statistic_id": entity_id,
                            "device_class": device_class,
                            "metadata_unit": metadata_unit,
                            "supported_unit": DEVICE_CLASS_UNITS[device_class],
                        },
                    ))
        elif state_class in STATE_CLASSES:
            if not is_entity_recorded(hass, state.entity_id):
                # Sensor is not recorded
                validation_result[entity_id].append(
                    statistics.ValidationIssue(
                        "entity_not_recorded",
                        {"statistic_id": entity_id},
                    ))
예제 #12
0
def _async_validate_usage_stat(
    hass: HomeAssistant,
    stat_value: str,
    allowed_device_classes: Sequence[str],
    allowed_units: Mapping[str, Sequence[str]],
    unit_error: str,
    result: list[ValidationIssue],
) -> None:
    """Validate a statistic."""
    has_entity_source = valid_entity_id(stat_value)

    if not has_entity_source:
        return

    if not recorder.is_entity_recorded(hass, stat_value):
        result.append(ValidationIssue(
            "recorder_untracked",
            stat_value,
        ))
        return

    state = hass.states.get(stat_value)

    if state is None:
        result.append(ValidationIssue(
            "entity_not_defined",
            stat_value,
        ))
        return

    if state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
        result.append(
            ValidationIssue("entity_unavailable", stat_value, state.state))
        return

    try:
        current_value: float | None = float(state.state)
    except ValueError:
        result.append(
            ValidationIssue("entity_state_non_numeric", stat_value,
                            state.state))
        return

    if current_value is not None and current_value < 0:
        result.append(
            ValidationIssue("entity_negative_state", stat_value,
                            current_value))

    device_class = state.attributes.get(ATTR_DEVICE_CLASS)
    if device_class not in allowed_device_classes:
        result.append(
            ValidationIssue(
                "entity_unexpected_device_class",
                stat_value,
                device_class,
            ))
    else:
        unit = state.attributes.get("unit_of_measurement")

        if device_class and unit not in allowed_units.get(device_class, []):
            result.append(ValidationIssue(unit_error, stat_value, unit))

    state_class = state.attributes.get(sensor.ATTR_STATE_CLASS)

    allowed_state_classes = [
        sensor.STATE_CLASS_MEASUREMENT,
        sensor.STATE_CLASS_TOTAL,
        sensor.STATE_CLASS_TOTAL_INCREASING,
    ]
    if state_class not in allowed_state_classes:
        result.append(
            ValidationIssue(
                "entity_unexpected_state_class",
                stat_value,
                state_class,
            ))

    if (state_class == sensor.STATE_CLASS_MEASUREMENT
            and sensor.ATTR_LAST_RESET not in state.attributes):
        result.append(
            ValidationIssue("entity_state_class_measurement_no_last_reset",
                            stat_value))
예제 #13
0
def _async_validate_usage_stat(
    hass: HomeAssistant,
    stat_value: str,
    allowed_units: Sequence[str],
    unit_error: str,
    result: list[ValidationIssue],
) -> None:
    """Validate a statistic."""
    has_entity_source = valid_entity_id(stat_value)

    if not has_entity_source:
        return

    if not recorder.is_entity_recorded(hass, stat_value):
        result.append(ValidationIssue(
            "recorder_untracked",
            stat_value,
        ))
        return

    state = hass.states.get(stat_value)

    if state is None:
        result.append(ValidationIssue(
            "entity_not_defined",
            stat_value,
        ))
        return

    if state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
        result.append(
            ValidationIssue("entity_unavailable", stat_value, state.state))
        return

    try:
        current_value: float | None = float(state.state)
    except ValueError:
        result.append(
            ValidationIssue("entity_state_non_numeric", stat_value,
                            state.state))
        return

    if current_value is not None and current_value < 0:
        result.append(
            ValidationIssue("entity_negative_state", stat_value,
                            current_value))

    unit = state.attributes.get("unit_of_measurement")

    if unit not in allowed_units:
        result.append(ValidationIssue(unit_error, stat_value, unit))

    state_class = state.attributes.get("state_class")

    supported_state_classes = [
        sensor.STATE_CLASS_MEASUREMENT,
        sensor.STATE_CLASS_TOTAL_INCREASING,
    ]
    if state_class not in supported_state_classes:
        result.append(
            ValidationIssue(
                "entity_unexpected_state_class_total_increasing",
                stat_value,
                state_class,
            ))
예제 #14
0
def _async_validate_energy_stat(
    hass: HomeAssistant, stat_value: str, result: list[ValidationIssue]
) -> None:
    """Validate a statistic."""
    has_entity_source = valid_entity_id(stat_value)

    if not has_entity_source:
        return

    if not recorder.is_entity_recorded(hass, stat_value):
        result.append(
            ValidationIssue(
                "recorder_untracked",
                stat_value,
            )
        )
        return

    state = hass.states.get(stat_value)

    if state is None:
        result.append(
            ValidationIssue(
                "entity_not_defined",
                stat_value,
            )
        )
        return

    if state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
        result.append(ValidationIssue("entity_unavailable", stat_value, state.state))
        return

    try:
        current_value: float | None = float(state.state)
    except ValueError:
        result.append(
            ValidationIssue("entity_state_non_numeric", stat_value, state.state)
        )
        return

    if current_value is not None and current_value < 0:
        result.append(
            ValidationIssue("entity_negative_state", stat_value, current_value)
        )

    unit = state.attributes.get("unit_of_measurement")

    if unit not in (ENERGY_KILO_WATT_HOUR, ENERGY_WATT_HOUR):
        result.append(
            ValidationIssue("entity_unexpected_unit_energy", stat_value, unit)
        )

    state_class = state.attributes.get("state_class")

    if state_class != sensor.STATE_CLASS_TOTAL_INCREASING:
        result.append(
            ValidationIssue(
                "entity_unexpected_state_class_total_increasing",
                stat_value,
                state_class,
            )
        )