Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions homeassistant/components/ai_task/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ generate_data:
required: true
selector:
text:
multiline: true
entity_id:
required: false
selector:
Expand Down
1 change: 0 additions & 1 deletion homeassistant/components/airq/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@
CONF_CLIP_NEGATIVE: Final = "clip_negatives"
DOMAIN: Final = "airq"
MANUFACTURER: Final = "CorantGmbH"
CONCENTRATION_GRAMS_PER_CUBIC_METER: Final = "g/m³"
ACTIVITY_BECQUEREL_PER_CUBIC_METER: Final = "Bq/m³"
UPDATE_INTERVAL: float = 10.0
3 changes: 0 additions & 3 deletions homeassistant/components/airq/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
"health_index": {
"default": "mdi:heart-pulse"
},
"absolute_humidity": {
"default": "mdi:water"
},
"oxygen": {
"default": "mdi:leaf"
},
Expand Down
8 changes: 3 additions & 5 deletions homeassistant/components/airq/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_GRAMS_PER_CUBIC_METER,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
Expand All @@ -28,10 +29,7 @@
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import AirQConfigEntry, AirQCoordinator
from .const import (
ACTIVITY_BECQUEREL_PER_CUBIC_METER,
CONCENTRATION_GRAMS_PER_CUBIC_METER,
)
from .const import ACTIVITY_BECQUEREL_PER_CUBIC_METER

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -195,7 +193,7 @@ class AirQEntityDescription(SensorEntityDescription):
),
AirQEntityDescription(
key="humidity_abs",
translation_key="absolute_humidity",
device_class=SensorDeviceClass.ABSOLUTE_HUMIDITY,
native_unit_of_measurement=CONCENTRATION_GRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("humidity_abs"),
Expand Down
3 changes: 0 additions & 3 deletions homeassistant/components/airq/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,6 @@
"health_index": {
"name": "Health index"
},
"absolute_humidity": {
"name": "Absolute humidity"
},
"hydrogen": {
"name": "Hydrogen"
},
Expand Down
26 changes: 20 additions & 6 deletions homeassistant/components/derivative/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@
async_entity_id_to_device_id,
async_remove_stale_devices_links_keep_entity_device,
)
from homeassistant.helpers.helper_integration import async_handle_source_entity_changes
from homeassistant.helpers.helper_integration import (
async_handle_source_entity_changes,
async_remove_helper_config_entry_from_source_device,
)

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Derivative from a config entry."""

# This can be removed in HA Core 2026.2
async_remove_stale_devices_links_keep_entity_device(
hass, entry.entry_id, entry.options[CONF_SOURCE]
)
Expand All @@ -29,20 +33,16 @@ def set_source_entity_id_or_uuid(source_entity_id: str) -> None:
options={**entry.options, CONF_SOURCE: source_entity_id},
)

async def source_entity_removed() -> None:
# The source entity has been removed, we need to clean the device links.
async_remove_stale_devices_links_keep_entity_device(hass, entry.entry_id, None)

entry.async_on_unload(
async_handle_source_entity_changes(
hass,
add_helper_config_entry_to_device=False,
helper_config_entry_id=entry.entry_id,
set_source_entity_id_or_uuid=set_source_entity_id_or_uuid,
source_device_id=async_entity_id_to_device_id(
hass, entry.options[CONF_SOURCE]
),
source_entity_id_or_uuid=entry.options[CONF_SOURCE],
source_entity_removed=source_entity_removed,
)
)
await hass.config_entries.async_forward_entry_setups(entry, (Platform.SENSOR,))
Expand Down Expand Up @@ -85,6 +85,20 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
config_entry, options=new_options, version=1, minor_version=2
)

if config_entry.minor_version < 3:
# Remove the derivative config entry from the source device
if source_device_id := async_entity_id_to_device_id(
hass, config_entry.options[CONF_SOURCE]
):
async_remove_helper_config_entry_from_source_device(
hass,
helper_config_entry_id=config_entry.entry_id,
source_device_id=source_device_id,
)
hass.config_entries.async_update_entry(
config_entry, version=1, minor_version=3
)

_LOGGER.debug(
"Migration to configuration version %s.%s successful",
config_entry.version,
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/derivative/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class ConfigFlowHandler(SchemaConfigFlowHandler, domain=DOMAIN):
options_flow = OPTIONS_FLOW

VERSION = 1
MINOR_VERSION = 2
MINOR_VERSION = 3

def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
"""Return config entry title."""
Expand Down
18 changes: 8 additions & 10 deletions homeassistant/components/derivative/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@
callback,
)
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers.device import async_device_info_to_link_from_entity
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.device import async_entity_id_to_device
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
Expand Down Expand Up @@ -118,17 +117,13 @@ async def async_setup_entry(
registry, config_entry.options[CONF_SOURCE]
)

device_info = async_device_info_to_link_from_entity(
hass,
source_entity_id,
)

if max_sub_interval_dict := config_entry.options.get(CONF_MAX_SUB_INTERVAL, None):
max_sub_interval = cv.time_period(max_sub_interval_dict)
else:
max_sub_interval = None

derivative_sensor = DerivativeSensor(
hass,
name=config_entry.title,
round_digits=int(config_entry.options[CONF_ROUND_DIGITS]),
source_entity=source_entity_id,
Expand All @@ -137,7 +132,6 @@ async def async_setup_entry(
unit_of_measurement=None,
unit_prefix=config_entry.options.get(CONF_UNIT_PREFIX),
unit_time=config_entry.options[CONF_UNIT_TIME],
device_info=device_info,
max_sub_interval=max_sub_interval,
)

Expand All @@ -152,6 +146,7 @@ async def async_setup_platform(
) -> None:
"""Set up the derivative sensor."""
derivative = DerivativeSensor(
hass,
name=config.get(CONF_NAME),
round_digits=config[CONF_ROUND_DIGITS],
source_entity=config[CONF_SOURCE],
Expand All @@ -174,6 +169,7 @@ class DerivativeSensor(RestoreSensor, SensorEntity):

def __init__(
self,
hass: HomeAssistant,
*,
name: str | None,
round_digits: int,
Expand All @@ -184,11 +180,13 @@ def __init__(
unit_time: UnitOfTime,
max_sub_interval: timedelta | None,
unique_id: str | None,
device_info: DeviceInfo | None = None,
) -> None:
"""Initialize the derivative sensor."""
self._attr_unique_id = unique_id
self._attr_device_info = device_info
self.device_entry = async_entity_id_to_device(
hass,
source_entity,
)
self._sensor_source_id = source_entity
self._round_digits = round_digits
self._attr_native_value = round(Decimal(0), round_digits)
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/mqtt/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2114,6 +2114,9 @@ def get_default(field_details: PlatformField) -> Any:
if schema_section is None:
data_schema.update(data_schema_element)
continue
if not data_schema_element:
# Do not show empty sections
continue
collapsed = (
not any(
(default := data_schema_fields[str(option)].default) is vol.UNDEFINED
Expand Down
10 changes: 10 additions & 0 deletions homeassistant/components/number/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import voluptuous as vol

from homeassistant.const import (
CONCENTRATION_GRAMS_PER_CUBIC_METER,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
Expand Down Expand Up @@ -78,6 +79,11 @@ class NumberDeviceClass(StrEnum):
"""Device class for numbers."""

# NumberDeviceClass should be aligned with SensorDeviceClass
ABSOLUTE_HUMIDITY = "absolute_humidity"
"""Absolute humidity.

Unit of measurement: `g/m³`, `mg/m³`
"""

APPARENT_POWER = "apparent_power"
"""Apparent power.
Expand Down Expand Up @@ -452,6 +458,10 @@ class NumberDeviceClass(StrEnum):

DEVICE_CLASSES_SCHEMA: Final = vol.All(vol.Lower, vol.Coerce(NumberDeviceClass))
DEVICE_CLASS_UNITS: dict[NumberDeviceClass, set[type[StrEnum] | str | None]] = {
NumberDeviceClass.ABSOLUTE_HUMIDITY: {
CONCENTRATION_GRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
},
NumberDeviceClass.APPARENT_POWER: set(UnitOfApparentPower),
NumberDeviceClass.AQI: {None},
NumberDeviceClass.AREA: set(UnitOfArea),
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/number/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"_": {
"default": "mdi:ray-vertex"
},
"absolute_humidity": {
"default": "mdi:water-opacity"
},
"apparent_power": {
"default": "mdi:flash"
},
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/number/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
}
}
},
"absolute_humidity": {
"name": "[%key:component::sensor::entity_component::absolute_humidity::name%]"
},
"apparent_power": {
"name": "[%key:component::sensor::entity_component::apparent_power::name%]"
},
Expand Down
10 changes: 7 additions & 3 deletions homeassistant/components/risco/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ async def async_setup_entry(
class RiscoAlarm(AlarmControlPanelEntity):
"""Representation of a Risco cloud partition."""

_attr_code_format = CodeFormat.NUMBER
_attr_has_entity_name = True
_attr_name = None

Expand All @@ -100,8 +99,13 @@ def __init__(
self._partition_id = partition_id
self._partition = partition
self._code = code
self._attr_code_arm_required = options[CONF_CODE_ARM_REQUIRED]
self._code_disarm_required = options[CONF_CODE_DISARM_REQUIRED]
arm_required = options[CONF_CODE_ARM_REQUIRED]
disarm_required = options[CONF_CODE_DISARM_REQUIRED]
self._attr_code_arm_required = arm_required
self._code_disarm_required = disarm_required
self._attr_code_format = (
CodeFormat.NUMBER if arm_required or disarm_required else None
)
self._risco_to_ha = options[CONF_RISCO_STATES_TO_HA]
self._ha_to_risco = options[CONF_HA_STATES_TO_RISCO]
for state in self._ha_to_risco:
Expand Down
14 changes: 14 additions & 0 deletions homeassistant/components/sensor/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import voluptuous as vol

from homeassistant.const import (
CONCENTRATION_GRAMS_PER_CUBIC_METER,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
Expand Down Expand Up @@ -107,6 +108,12 @@ class SensorDeviceClass(StrEnum):
"""

# Numerical device classes, these should be aligned with NumberDeviceClass
ABSOLUTE_HUMIDITY = "absolute_humidity"
"""Absolute humidity.

Unit of measurement: `g/m³`, `mg/m³`
"""

APPARENT_POWER = "apparent_power"
"""Apparent power.

Expand Down Expand Up @@ -521,6 +528,7 @@ class SensorStateClass(StrEnum):
STATE_CLASSES: Final[list[str]] = [cls.value for cls in SensorStateClass]

UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] = {
SensorDeviceClass.ABSOLUTE_HUMIDITY: MassVolumeConcentrationConverter,
SensorDeviceClass.AREA: AreaConverter,
SensorDeviceClass.ATMOSPHERIC_PRESSURE: PressureConverter,
SensorDeviceClass.BLOOD_GLUCOSE_CONCENTRATION: BloodGlucoseConcentrationConverter,
Expand Down Expand Up @@ -554,6 +562,10 @@ class SensorStateClass(StrEnum):
}

DEVICE_CLASS_UNITS: dict[SensorDeviceClass, set[type[StrEnum] | str | None]] = {
SensorDeviceClass.ABSOLUTE_HUMIDITY: {
CONCENTRATION_GRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
},
SensorDeviceClass.APPARENT_POWER: set(UnitOfApparentPower),
SensorDeviceClass.AQI: {None},
SensorDeviceClass.AREA: set(UnitOfArea),
Expand Down Expand Up @@ -651,6 +663,7 @@ class SensorStateClass(StrEnum):
# have 0 decimals, that one should be used and not mW, even though mW also should have
# 0 decimals. Otherwise the smaller units will have more decimals than expected.
UNITS_PRECISION = {
SensorDeviceClass.ABSOLUTE_HUMIDITY: (CONCENTRATION_GRAMS_PER_CUBIC_METER, 1),
SensorDeviceClass.APPARENT_POWER: (UnitOfApparentPower.VOLT_AMPERE, 0),
SensorDeviceClass.AREA: (UnitOfArea.SQUARE_CENTIMETERS, 0),
SensorDeviceClass.ATMOSPHERIC_PRESSURE: (UnitOfPressure.PA, 0),
Expand Down Expand Up @@ -691,6 +704,7 @@ class SensorStateClass(StrEnum):
}

DEVICE_CLASS_STATE_CLASSES: dict[SensorDeviceClass, set[SensorStateClass]] = {
SensorDeviceClass.ABSOLUTE_HUMIDITY: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.APPARENT_POWER: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.AQI: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.AREA: set(SensorStateClass),
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/sensor/device_condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

DEVICE_CLASS_NONE = "none"

CONF_IS_ABSOLUTE_HUMIDITY = "is_absolute_humidity"
CONF_IS_APPARENT_POWER = "is_apparent_power"
CONF_IS_AQI = "is_aqi"
CONF_IS_AREA = "is_area"
Expand Down Expand Up @@ -88,6 +89,7 @@
CONF_IS_WIND_SPEED = "is_wind_speed"

ENTITY_CONDITIONS = {
SensorDeviceClass.ABSOLUTE_HUMIDITY: [{CONF_TYPE: CONF_IS_ABSOLUTE_HUMIDITY}],
SensorDeviceClass.APPARENT_POWER: [{CONF_TYPE: CONF_IS_APPARENT_POWER}],
SensorDeviceClass.AQI: [{CONF_TYPE: CONF_IS_AQI}],
SensorDeviceClass.AREA: [{CONF_TYPE: CONF_IS_AREA}],
Expand Down Expand Up @@ -159,6 +161,7 @@
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
vol.Required(CONF_TYPE): vol.In(
[
CONF_IS_ABSOLUTE_HUMIDITY,
CONF_IS_APPARENT_POWER,
CONF_IS_AQI,
CONF_IS_AREA,
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/sensor/device_trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

DEVICE_CLASS_NONE = "none"

CONF_ABSOLUTE_HUMIDITY = "absolute_humidity"
CONF_APPARENT_POWER = "apparent_power"
CONF_AQI = "aqi"
CONF_AREA = "area"
Expand Down Expand Up @@ -87,6 +88,7 @@
CONF_WIND_SPEED = "wind_speed"

ENTITY_TRIGGERS = {
SensorDeviceClass.ABSOLUTE_HUMIDITY: [{CONF_TYPE: CONF_ABSOLUTE_HUMIDITY}],
SensorDeviceClass.APPARENT_POWER: [{CONF_TYPE: CONF_APPARENT_POWER}],
SensorDeviceClass.AQI: [{CONF_TYPE: CONF_AQI}],
SensorDeviceClass.AREA: [{CONF_TYPE: CONF_AREA}],
Expand Down Expand Up @@ -159,6 +161,7 @@
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
vol.Required(CONF_TYPE): vol.In(
[
CONF_ABSOLUTE_HUMIDITY,
CONF_APPARENT_POWER,
CONF_AQI,
CONF_AREA,
Expand Down
Loading
Loading