Skip to content
Open
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: 2 additions & 0 deletions homeassistant/components/blebox/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ def __init__(
"""Initialize a BleBox binary sensor feature."""
super().__init__(feature)
self.entity_description = description
if feature.name:
self._attr_name = feature.name

@property
def is_on(self) -> bool:
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/blebox/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ async def async_setup_entry(
class BleBoxButtonEntity(BleBoxEntity[blebox_uniapi.button.Button], ButtonEntity):
"""Representation of BleBox buttons."""

_attr_name = None

def __init__(self, feature: blebox_uniapi.button.Button) -> None:
"""Initialize a BleBox button feature."""
super().__init__(feature)
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/blebox/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ async def async_setup_entry(
class BleBoxClimateEntity(BleBoxEntity[blebox_uniapi.climate.Climate], ClimateEntity):
"""Representation of a BleBox climate feature (saunaBox)."""

_attr_name = None
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/blebox/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ async def async_setup_entry(
class BleBoxCoverEntity(BleBoxEntity[blebox_uniapi.cover.Cover], CoverEntity):
"""Representation of a BleBox cover feature."""

_attr_name = None

def __init__(self, feature: blebox_uniapi.cover.Cover) -> None:
"""Initialize a BleBox cover feature."""
super().__init__(feature)
Expand Down
5 changes: 3 additions & 2 deletions homeassistant/components/blebox/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
class BleBoxEntity[_FeatureT: Feature](Entity):
"""Implements a common class for entities representing a BleBox feature."""

_attr_has_entity_name = True

def __init__(self, feature: _FeatureT) -> None:
"""Initialize a BleBox entity."""
self._feature = feature
self._attr_name = feature.full_name
self._attr_unique_id = feature.unique_id
product = feature.product
self._attr_device_info = DeviceInfo(
Expand All @@ -36,4 +37,4 @@ async def async_update(self) -> None:
try:
await self._feature.async_update()
except Error as ex:
_LOGGER.error("Updating '%s' failed: %s", self.name, ex)
_LOGGER.error("Updating '%s' failed: %s", self._feature.full_name, ex)
9 changes: 7 additions & 2 deletions homeassistant/components/blebox/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ def __init__(self, feature: blebox_uniapi.light.Light) -> None:
super().__init__(feature)
if feature.effect_list:
self._attr_supported_features = LightEntityFeature.EFFECT
if feature.index is not None:
self._attr_translation_key = "channel"
self._attr_translation_placeholders = {"index": str(feature.index + 1)}
else:
self._attr_name = None

@property
def is_on(self) -> bool:
Expand Down Expand Up @@ -205,7 +210,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
await self._feature.async_on(value)
except ValueError as exc:
raise ValueError(
f"Turning on '{self.name}' failed: Bad value {value}"
f"Turning on '{self._feature.full_name}' failed: Bad value {value}"
) from exc

if effect is not None:
Expand All @@ -214,7 +219,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
await self._feature.async_api_command("effect", effect_value)
except ValueError as exc:
raise ValueError(
f"Turning on with effect '{self.name}' failed: {effect} not in"
f"Turning on with effect '{self._feature.full_name}' failed: {effect} not in"
" effect list."
) from exc

Expand Down
76 changes: 56 additions & 20 deletions homeassistant/components/blebox/sensor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""BleBox sensor entities."""

from collections import Counter
from dataclasses import dataclass
from datetime import datetime, timedelta

import blebox_uniapi.sensor
Expand Down Expand Up @@ -33,85 +35,105 @@
SCAN_INTERVAL = timedelta(seconds=5)


@dataclass(kw_only=True, frozen=True)
class BleBoxSensorEntityDescription(SensorEntityDescription):
"""Describes a BleBox sensor entity."""

indexed_translation_key: str | None = None


SENSOR_TYPES = (
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="pm1",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="pm2_5",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="pm10",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="temperature",
indexed_translation_key="temperature_n",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="powerConsumption",
translation_key="power_consumption",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=2,
icon="mdi:lightning-bolt",
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="wind",
device_class=SensorDeviceClass.WIND_SPEED,
native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="illuminance",
device_class=SensorDeviceClass.ILLUMINANCE,
native_unit_of_measurement=LIGHT_LUX,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="forwardActiveEnergy",
translation_key="forward_active_energy",
indexed_translation_key="forward_active_energy_n",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="reverseActiveEnergy",
translation_key="reverse_active_energy",
indexed_translation_key="reverse_active_energy_n",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="reactivePower",
device_class=SensorDeviceClass.POWER,
indexed_translation_key="reactive_power_n",
device_class=SensorDeviceClass.REACTIVE_POWER,
native_unit_of_measurement=UnitOfReactivePower.VOLT_AMPERE_REACTIVE,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="activePower",
translation_key="active_power",
indexed_translation_key="active_power_n",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="apparentPower",
indexed_translation_key="apparent_power_n",
device_class=SensorDeviceClass.APPARENT_POWER,
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="voltage",
indexed_translation_key="voltage_n",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="current",
indexed_translation_key="current_n",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE,
),
SensorEntityDescription(
BleBoxSensorEntityDescription(
key="frequency",
indexed_translation_key="frequency_n",
device_class=SensorDeviceClass.FREQUENCY,
native_unit_of_measurement=UnitOfFrequency.HERTZ,
),
Expand All @@ -124,9 +146,17 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up a BleBox entry."""
features = config_entry.runtime_data.features.get("sensors", [])
counts = Counter(f.device_class for f in features)
entities = [
BleBoxSensorEntity(feature, description)
for feature in config_entry.runtime_data.features.get("sensors", [])
BleBoxSensorEntity(
feature,
description,
feature.index
if counts[feature.device_class] > 1 and feature.index
else None,
)
for feature in features
for description in SENSOR_TYPES
if description.key == feature.device_class
]
Expand All @@ -139,11 +169,17 @@ class BleBoxSensorEntity(BleBoxEntity[blebox_uniapi.sensor.BaseSensor], SensorEn
def __init__(
self,
feature: blebox_uniapi.sensor.BaseSensor,
description: SensorEntityDescription,
description: BleBoxSensorEntityDescription,
index: int | None = None,
) -> None:
"""Initialize a BleBox sensor feature."""
super().__init__(feature)
self.entity_description = description
if feature.name:
self._attr_name = feature.name
elif index is not None and description.indexed_translation_key:
self._attr_translation_key = description.indexed_translation_key
self._attr_translation_placeholders = {"index": str(index)}

@property
def native_value(self):
Expand Down
26 changes: 26 additions & 0 deletions homeassistant/components/blebox/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,35 @@
"port": "[%key:common::config_flow::data::port%]",
"username": "[%key:common::config_flow::data::username%]"
},
"data_description": {
"host": "The IP address of your BleBox device.",
"password": "The password for your BleBox device.",
"port": "The port of your BleBox device.",
"username": "The username for your BleBox device."
},
"description": "Set up your BleBox to integrate with Home Assistant.",
"title": "Set up your BleBox device"
}
}
},
"entity": {
"light": {
"channel": { "name": "Channel {index}" }
},
"sensor": {
"active_power": { "name": "Active power" },
"active_power_n": { "name": "Active power {index}" },
"apparent_power_n": { "name": "Apparent power {index}" },
"current_n": { "name": "Current {index}" },
"forward_active_energy": { "name": "Forward active energy" },
"forward_active_energy_n": { "name": "Forward active energy {index}" },
"frequency_n": { "name": "Frequency {index}" },
"power_consumption": { "name": "Energy (last 1 h)" },
"reactive_power_n": { "name": "Reactive power {index}" },
"reverse_active_energy": { "name": "Reverse active energy" },
"reverse_active_energy_n": { "name": "Reverse active energy {index}" },
"temperature_n": { "name": "Temperature {index}" },
"voltage_n": { "name": "Voltage {index}" }
}
}
}
8 changes: 8 additions & 0 deletions homeassistant/components/blebox/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ class BleBoxSwitchEntity(BleBoxEntity[blebox_uniapi.switch.Switch], SwitchEntity

_attr_device_class = SwitchDeviceClass.SWITCH

_attr_name = None

def __init__(self, feature: blebox_uniapi.switch.Switch) -> None:
"""Initialize a BleBox switch feature."""
super().__init__(feature)
if feature.name:
self._attr_name = feature.name

@property
def is_on(self) -> bool | None:
"""Return whether switch is on."""
Expand Down
26 changes: 24 additions & 2 deletions tests/components/blebox/test_binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ def airsensor_fixture() -> tuple[AsyncMock, str]:
unique_id="BleBox-windRainSensor-ea68e74f4f49-0.rain",
full_name="windRainSensor-0.rain",
device_class="moisture",
index=None,
)
type(feature).name = PropertyMock(return_value=None)
product = feature.product
type(product).name = PropertyMock(return_value="My rain sensor")
type(product).model = PropertyMock(return_value="rainSensor")
return feature, "binary_sensor.my_rain_sensor_windrainsensor_0_rain"
return feature, "binary_sensor.my_rain_sensor_moisture"


async def test_init(
Expand All @@ -38,11 +40,31 @@ async def test_init(
assert entry.unique_id == "BleBox-windRainSensor-ea68e74f4f49-0.rain"

state = hass.states.get(entity_id)
assert state.name == "My rain sensor windRainSensor-0.rain"
assert state.name == "My rain sensor Moisture"

assert state.attributes[ATTR_DEVICE_CLASS] == BinarySensorDeviceClass.MOISTURE
assert state.state == STATE_ON

device = device_registry.async_get(entry.device_id)

assert device.name == "My rain sensor"


async def test_binary_sensor_with_name(hass: HomeAssistant) -> None:
"""Test that a binary sensor with a feature name uses it as the entity name."""
feature = mock_feature(
"binary_sensors",
blebox_uniapi.binary_sensor.Rain,
unique_id="BleBox-windRainSensor-ea68e74f4f49-0.rain",
full_name="windRainSensor-0.rain",
device_class="moisture",
index=0,
)
type(feature).name = PropertyMock(return_value="Front yard")
product = feature.product
type(product).name = PropertyMock(return_value="My rain sensor")
type(product).model = PropertyMock(return_value="rainSensor")

await async_setup_entity(hass, "binary_sensor.my_rain_sensor_front_yard")
state = hass.states.get("binary_sensor.my_rain_sensor_front_yard")
assert state.name == "My rain sensor Front yard"
4 changes: 2 additions & 2 deletions tests/components/blebox/test_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def tv_lift_box_fixture(caplog: pytest.LogCaptureFixture):
type(product).model = PropertyMock(return_value="tvLiftBox")
type(product)._query_string = PropertyMock(return_value="open_or_stop")

return (feature, "button.my_tvliftbox_tvliftbox_open_or_stop")
return (feature, "button.my_tvliftbox")


async def test_tvliftbox_init(
Expand All @@ -53,7 +53,7 @@ async def test_tvliftbox_init(

assert entry.unique_id == "BleBox-tvLiftBox-4a3fdaad90aa-open_or_stop"

assert state.name == "My tvLiftBox tvLiftBox-open_or_stop"
assert state.name == "My tvLiftBox"


@pytest.mark.parametrize("input", query_icon_matching)
Expand Down
6 changes: 3 additions & 3 deletions tests/components/blebox/test_climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def saunabox_fixture():
product = feature.product
type(product).name = PropertyMock(return_value="My sauna")
type(product).model = PropertyMock(return_value="saunaBox")
return (feature, "climate.my_sauna_saunabox_thermostat")
return (feature, "climate.my_sauna")


@pytest.fixture(name="thermobox")
Expand All @@ -75,7 +75,7 @@ def thermobox_fixture():
product = feature.product
type(product).name = PropertyMock(return_value="My thermo")
type(product).model = PropertyMock(return_value="thermoBox")
return (feature, "climate.my_thermo_thermobox_thermostat")
return (feature, "climate.my_thermo")


async def test_init(
Expand All @@ -88,7 +88,7 @@ async def test_init(
assert entry.unique_id == "BleBox-saunaBox-1afe34db9437-thermostat"

state = hass.states.get(entity_id)
assert state.name == "My sauna saunaBox-thermostat"
assert state.name == "My sauna"

supported_features = state.attributes[ATTR_SUPPORTED_FEATURES]
assert supported_features & ClimateEntityFeature.TARGET_TEMPERATURE
Expand Down
Loading