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
2 changes: 1 addition & 1 deletion homeassistant/components/airq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .const import CONF_CLIP_NEGATIVE, CONF_RETURN_AVERAGE
from .coordinator import AirQCoordinator

PLATFORMS: list[Platform] = [Platform.SENSOR]
PLATFORMS: list[Platform] = [Platform.NUMBER, Platform.SENSOR]

AirQConfigEntry = ConfigEntry[AirQCoordinator]

Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/airq/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ async def _async_update_data(self) -> dict:
return_average=self.return_average,
clip_negative_values=self.clip_negative,
)
data["brightness"] = await self.airq.get_current_brightness()
if warming_up_sensors := identify_warming_up_sensors(data):
_LOGGER.debug(
"Following sensors are still warming up: %s", warming_up_sensors
Expand Down
85 changes: 85 additions & 0 deletions homeassistant/components/airq/number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""Definition of air-Q number platform used to control the LED strips."""

from __future__ import annotations

from collections.abc import Awaitable, Callable
from dataclasses import dataclass
import logging

from aioairq.core import AirQ

from homeassistant.components.number import NumberEntity, NumberEntityDescription
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import AirQConfigEntry, AirQCoordinator

_LOGGER = logging.getLogger(__name__)


@dataclass(frozen=True, kw_only=True)
class AirQBrightnessDescription(NumberEntityDescription):
"""Describes AirQ number entity responsible for brightness control."""

value: Callable[[dict], float]
set_value: Callable[[AirQ, float], Awaitable[None]]


AIRQ_LED_BRIGHTNESS = AirQBrightnessDescription(
key="airq_led_brightness",
translation_key="airq_led_brightness",
native_min_value=0.0,
native_max_value=100.0,
native_step=1.0,
native_unit_of_measurement=PERCENTAGE,
value=lambda data: data["brightness"],
set_value=lambda device, value: device.set_current_brightness(value),
)


async def async_setup_entry(
hass: HomeAssistant,
entry: AirQConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up number entities: a single entity for the LEDs."""

coordinator = entry.runtime_data
entities = [AirQLEDBrightness(coordinator, AIRQ_LED_BRIGHTNESS)]

async_add_entities(entities)


class AirQLEDBrightness(CoordinatorEntity[AirQCoordinator], NumberEntity):
"""Representation of the LEDs from a single AirQ."""

_attr_has_entity_name = True

def __init__(
self,
coordinator: AirQCoordinator,
description: AirQBrightnessDescription,
) -> None:
"""Initialize a single sensor."""
super().__init__(coordinator)
self.entity_description: AirQBrightnessDescription = description

self._attr_device_info = coordinator.device_info
self._attr_unique_id = f"{coordinator.device_id}_{description.key}"

@property
def native_value(self) -> float:
"""Return the brightness of the LEDs in %."""
return self.entity_description.value(self.coordinator.data)

async def async_set_native_value(self, value: float) -> None:
"""Set the brightness of the LEDs to the value in %."""
_LOGGER.debug(
"Changing LED brighntess from %.0f%% to %.0f%%",
self.coordinator.data["brightness"],
value,
)
await self.entity_description.set_value(self.coordinator.airq, value)
await self.coordinator.async_request_refresh()
5 changes: 5 additions & 0 deletions homeassistant/components/airq/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
}
},
"entity": {
"number": {
"airq_led_brightness": {
"name": "LED brightness"
}
},
"sensor": {
"acetaldehyde": {
"name": "Acetaldehyde"
Expand Down
17 changes: 17 additions & 0 deletions homeassistant/components/enphase_envoy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from __future__ import annotations

from typing import TYPE_CHECKING

from pyenphase import Envoy

from homeassistant.const import CONF_HOST
Expand Down Expand Up @@ -42,6 +44,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: EnphaseConfigEntry) -> b
},
)

# register envoy before via_device is used
device_registry = dr.async_get(hass)
if TYPE_CHECKING:
assert envoy.serial_number
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, envoy.serial_number)},
manufacturer="Enphase",
name=coordinator.name,
model=envoy.envoy_model,
sw_version=str(envoy.firmware),
hw_version=envoy.part_number,
serial_number=envoy.serial_number,
)

entry.runtime_data = coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/miele/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -1330,4 +1330,5 @@ class PlatePowerStep(MieleEnum):
plate_step_17 = 17
plate_step_18 = 18
plate_step_boost = 117, 118, 218
plate_step_boost_2 = 217
missing2none = -9999
3 changes: 2 additions & 1 deletion homeassistant/components/miele/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@
"plate_step_16": "mdi:circle-slice-7",
"plate_step_17": "mdi:circle-slice-8",
"plate_step_18": "mdi:circle-slice-8",
"plate_step_boost": "mdi:alpha-b-circle-outline"
"plate_step_boost": "mdi:alpha-b-circle-outline",
"plate_step_boost_2": "mdi:alpha-b-circle"
}
},
"program_type": {
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/miele/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@
"plate_step_16": "8\u2022",
"plate_step_17": "9",
"plate_step_18": "9\u2022",
"plate_step_boost": "Boost"
"plate_step_boost": "Boost",
"plate_step_boost_2": "Boost 2"
}
},
"drying_step": {
Expand Down
7 changes: 5 additions & 2 deletions homeassistant/components/powerfox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import asyncio

from powerfox import Powerfox, PowerfoxConnectionError
from powerfox import DeviceType, Powerfox, PowerfoxConnectionError

from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform
from homeassistant.core import HomeAssistant
Expand All @@ -31,7 +31,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: PowerfoxConfigEntry) ->
raise ConfigEntryNotReady from err

coordinators: list[PowerfoxDataUpdateCoordinator] = [
PowerfoxDataUpdateCoordinator(hass, entry, client, device) for device in devices
PowerfoxDataUpdateCoordinator(hass, entry, client, device)
for device in devices
# Filter out gas meter devices (Powerfox FLOW adapters) as they are not yet supported and cause integration failures
if device.type != DeviceType.GAS_METER
]

await asyncio.gather(
Expand Down
115 changes: 80 additions & 35 deletions homeassistant/components/ruuvitag_ble/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from sensor_state_data import (
DeviceKey,
SensorDescription,
SensorDeviceClass as SSDSensorDeviceClass,
SensorUpdate,
Units,
Expand Down Expand Up @@ -32,53 +31,108 @@
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info

from .const import DOMAIN

SENSOR_DESCRIPTIONS = {
(SSDSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription(
"temperature": SensorEntityDescription(
key=f"{SSDSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
),
(SSDSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription(
"humidity": SensorEntityDescription(
key=f"{SSDSensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
(SSDSensorDeviceClass.PRESSURE, Units.PRESSURE_HPA): SensorEntityDescription(
"pressure": SensorEntityDescription(
key=f"{SSDSensorDeviceClass.PRESSURE}_{Units.PRESSURE_HPA}",
device_class=SensorDeviceClass.PRESSURE,
native_unit_of_measurement=UnitOfPressure.HPA,
state_class=SensorStateClass.MEASUREMENT,
),
(
SSDSensorDeviceClass.VOLTAGE,
Units.ELECTRIC_POTENTIAL_MILLIVOLT,
): SensorEntityDescription(
"voltage": SensorEntityDescription(
key=f"{SSDSensorDeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_MILLIVOLT}",
native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
(
SSDSensorDeviceClass.SIGNAL_STRENGTH,
Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
): SensorEntityDescription(
"signal_strength": SensorEntityDescription(
key=f"{SSDSensorDeviceClass.SIGNAL_STRENGTH}_{Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT}",
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
(SSDSensorDeviceClass.COUNT, None): SensorEntityDescription(
"movement_counter": SensorEntityDescription(
key="movement_counter",
translation_key="movement_counter",
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
),
# Acceleration keys exported in newer versions of ruuvitag-ble
"acceleration_x": SensorEntityDescription(
key=f"acceleration_x_{Units.ACCELERATION_METERS_PER_SQUARE_SECOND}",
translation_key="acceleration_x",
native_unit_of_measurement=Units.ACCELERATION_METERS_PER_SQUARE_SECOND,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
"acceleration_y": SensorEntityDescription(
key=f"acceleration_y_{Units.ACCELERATION_METERS_PER_SQUARE_SECOND}",
translation_key="acceleration_y",
native_unit_of_measurement=Units.ACCELERATION_METERS_PER_SQUARE_SECOND,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
"acceleration_z": SensorEntityDescription(
key=f"acceleration_z_{Units.ACCELERATION_METERS_PER_SQUARE_SECOND}",
translation_key="acceleration_z",
native_unit_of_measurement=Units.ACCELERATION_METERS_PER_SQUARE_SECOND,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
"acceleration_total": SensorEntityDescription(
key=f"acceleration_total_{Units.ACCELERATION_METERS_PER_SQUARE_SECOND}",
translation_key="acceleration_total",
native_unit_of_measurement=Units.ACCELERATION_METERS_PER_SQUARE_SECOND,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
# Keys exported for dataformat 06 sensors in newer versions of ruuvitag-ble
"pm25": SensorEntityDescription(
key=f"{SSDSensorDeviceClass.PM25}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
"carbon_dioxide": SensorEntityDescription(
key=f"{SSDSensorDeviceClass.CO2}_{Units.CONCENTRATION_PARTS_PER_MILLION}",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=Units.CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
"illuminance": SensorEntityDescription(
key=f"{SSDSensorDeviceClass.ILLUMINANCE}_{Units.LIGHT_LUX}",
device_class=SensorDeviceClass.ILLUMINANCE,
native_unit_of_measurement=Units.LIGHT_LUX,
state_class=SensorStateClass.MEASUREMENT,
),
"voc_index": SensorEntityDescription(
key="voc_index",
translation_key="voc_index",
state_class=SensorStateClass.MEASUREMENT,
),
"nox_index": SensorEntityDescription(
key="nox_index",
translation_key="nox_index",
state_class=SensorStateClass.MEASUREMENT,
),
}


Expand All @@ -89,37 +143,28 @@ def _device_key_to_bluetooth_entity_key(
return PassiveBluetoothEntityKey(device_key.key, device_key.device_id)


def _to_sensor_key(
description: SensorDescription,
) -> tuple[SSDSensorDeviceClass, Units | None]:
assert description.device_class is not None
return (description.device_class, description.native_unit_of_measurement)


def sensor_update_to_bluetooth_data_update(
sensor_update: SensorUpdate,
) -> PassiveBluetoothDataUpdate:
"""Convert a sensor update to a bluetooth data update."""
entity_descriptions: dict[PassiveBluetoothEntityKey, EntityDescription] = {}
entity_data = {}
for device_key, sensor_values in sensor_update.entity_values.items():
bek = _device_key_to_bluetooth_entity_key(device_key)
entity_data[bek] = sensor_values.native_value
for device_key in sensor_update.entity_descriptions:
bek = _device_key_to_bluetooth_entity_key(device_key)
if sk_description := SENSOR_DESCRIPTIONS.get(device_key.key):
entity_descriptions[bek] = sk_description

return PassiveBluetoothDataUpdate(
devices={
device_id: sensor_device_info_to_hass_device_info(device_info)
for device_id, device_info in sensor_update.devices.items()
},
entity_descriptions={
_device_key_to_bluetooth_entity_key(device_key): SENSOR_DESCRIPTIONS[
_to_sensor_key(description)
]
for device_key, description in sensor_update.entity_descriptions.items()
if _to_sensor_key(description) in SENSOR_DESCRIPTIONS
},
entity_data={
_device_key_to_bluetooth_entity_key(device_key): sensor_values.native_value
for device_key, sensor_values in sensor_update.entity_values.items()
},
entity_names={
_device_key_to_bluetooth_entity_key(device_key): sensor_values.name
for device_key, sensor_values in sensor_update.entity_values.items()
},
entity_descriptions=entity_descriptions,
entity_data=entity_data,
entity_names={},
)


Expand Down
Loading
Loading