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/airos/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "silver",
"requirements": ["airos==0.5.6"]
"requirements": ["airos==0.6.0"]
}
8 changes: 7 additions & 1 deletion homeassistant/components/template/template_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,13 @@ def name(self) -> str:
"""Name of this state."""
return "<None>"

variables = {"this": DummyState()}
# Render the current variables and add a dummy this variable to them.
variables = (
self._run_variables
if isinstance(self._run_variables, dict)
else self._run_variables.async_render(self.hass, {})
)
variables = {"this": DummyState(), **variables}

# Try to render the name as it can influence the entity ID
self._attr_name = None
Expand Down
16 changes: 9 additions & 7 deletions homeassistant/components/vicare/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,13 @@ def __init__(
gateway_serial = device_config.getConfig().serial
device_id = device_config.getId()
model = device_config.getModel().replace("_", " ")
via_device_identifier: tuple[str, str] = ("", "")

identifier = (
f"{gateway_serial}_{device_serial.replace('-', '_')}"
if device_serial is not None
else f"{gateway_serial}_{device_id}"
)

if device_serial is not None and device_serial.startswith("zigbee-"):
parts = device_serial.split("-")
if len(parts) == 3: # expect format zigbee-<zigbee-ieee>-<channel-id>
via_device_identifier = (DOMAIN, f"{gateway_serial}_zigbee_{parts[1]}")

self._api: PyViCareDevice | PyViCareHeatingDeviceComponent = (
component if component else device
)
Expand All @@ -56,5 +50,13 @@ def __init__(
manufacturer="Viessmann",
model=model,
configuration_url=VIESSMANN_DEVELOPER_PORTAL,
via_device=via_device_identifier,
)

if device_serial and device_serial.startswith("zigbee-"):
parts = device_serial.split("-", 2)
if len(parts) == 3:
_, zigbee_ieee, _ = parts
self._attr_device_info["via_device"] = (
DOMAIN,
f"{gateway_serial}_zigbee_{zigbee_ieee}",
)
22 changes: 14 additions & 8 deletions homeassistant/components/vicare/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ class ViCareSensorEntityDescription(SensorEntityDescription, ViCareRequiredKeysM
unit_getter: Callable[[PyViCareDevice], str | None] | None = None


SUPPLY_TEMPERATURE_SENSOR: ViCareSensorEntityDescription = (
ViCareSensorEntityDescription(
key="supply_temperature",
translation_key="supply_temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_getter=lambda api: api.getSupplyTemperature(),
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
)
)


GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
ViCareSensorEntityDescription(
key="outside_temperature",
Expand Down Expand Up @@ -978,17 +990,11 @@ class ViCareSensorEntityDescription(SensorEntityDescription, ViCareRequiredKeysM
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_getter=lambda api: api.getHydraulicSeparatorTemperature(),
),
SUPPLY_TEMPERATURE_SENSOR,
)

CIRCUIT_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
ViCareSensorEntityDescription(
key="supply_temperature",
translation_key="supply_temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_getter=lambda api: api.getSupplyTemperature(),
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
SUPPLY_TEMPERATURE_SENSOR,
)

BURNER_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
Expand Down
21 changes: 21 additions & 0 deletions homeassistant/components/victron_remote_monitoring/energy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Victron Remote Monitoring energy platform."""

from __future__ import annotations

from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant


async def async_get_solar_forecast(
hass: HomeAssistant, config_entry_id: str
) -> dict[str, dict[str, float | int]] | None:
"""Get solar forecast for a config entry ID."""
if (
entry := hass.config_entries.async_get_entry(config_entry_id)
) is None or entry.state != ConfigEntryState.LOADED:
return None
data = entry.runtime_data.data.solar
if data is None:
return None

return {"wh_hours": data.get_dict_isoformat}
1 change: 1 addition & 0 deletions homeassistant/components/whirlpool/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ async def authenticate(
not appliances_manager.aircons
and not appliances_manager.washers
and not appliances_manager.dryers
and not appliances_manager.ovens
):
return "no_appliances"

Expand Down
29 changes: 29 additions & 0 deletions homeassistant/components/whirlpool/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging

from whirlpool.appliance import Appliance
from whirlpool.oven import Cavity as OvenCavity, Oven

from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
Expand Down Expand Up @@ -64,3 +65,31 @@ def _check_service_request(result: bool) -> None:
translation_domain=DOMAIN,
translation_key="request_failed",
)


class WhirlpoolOvenEntity(WhirlpoolEntity):
"""Base class for Whirlpool oven entities."""

_appliance: Oven

def __init__(
self,
appliance: Oven,
cavity: OvenCavity,
translation_key_base: str | None,
unique_id_suffix: str = "",
) -> None:
"""Initialize the entity."""
self.cavity = cavity
cavity_suffix = ""
if appliance.get_oven_cavity_exists(
OvenCavity.Upper
) and appliance.get_oven_cavity_exists(OvenCavity.Lower):
if cavity == OvenCavity.Upper:
cavity_suffix = "_upper"
elif cavity == OvenCavity.Lower:
cavity_suffix = "_lower"
super().__init__(
appliance, unique_id_suffix=f"{unique_id_suffix}{cavity_suffix}"
)
self._attr_translation_key = f"{translation_key_base}{cavity_suffix}"
9 changes: 9 additions & 0 deletions homeassistant/components/whirlpool/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
},
"dryer_state": {
"default": "mdi:tumble-dryer"
},
"oven_state": {
"default": "mdi:stove"
},
"oven_state_upper": {
"default": "mdi:stove"
},
"oven_state_lower": {
"default": "mdi:stove"
}
}
}
Expand Down
119 changes: 118 additions & 1 deletion homeassistant/components/whirlpool/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,29 @@

from whirlpool.appliance import Appliance
from whirlpool.dryer import Dryer, MachineState as DryerMachineState
from whirlpool.oven import (
Cavity as OvenCavity,
CavityState as OvenCavityState,
CookMode,
Oven,
)
from whirlpool.washer import MachineState as WasherMachineState, Washer

from homeassistant.components.sensor import (
RestoreSensor,
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.util.dt import utcnow

from . import WhirlpoolConfigEntry
from .entity import WhirlpoolEntity
from .entity import WhirlpoolEntity, WhirlpoolOvenEntity

PARALLEL_UPDATES = 1
SCAN_INTERVAL = timedelta(minutes=5)
Expand Down Expand Up @@ -88,6 +96,23 @@
STATE_CYCLE_SPINNING = "cycle_spinning"
STATE_CYCLE_WASHING = "cycle_washing"

OVEN_CAVITY_STATE = {
OvenCavityState.Standby: "standby",
OvenCavityState.Preheating: "preheating",
OvenCavityState.Cooking: "cooking",
}

OVEN_COOK_MODE = {
CookMode.Standby: "standby",
CookMode.Bake: "bake",
CookMode.ConvectBake: "convection_bake",
CookMode.Broil: "broil",
CookMode.ConvectBroil: "convection_broil",
CookMode.ConvectRoast: "convection_roast",
CookMode.KeepWarm: "keep_warm",
CookMode.AirFry: "air_fry",
}


def washer_state(washer: Washer) -> str | None:
"""Determine correct states for a washer."""
Expand Down Expand Up @@ -183,6 +208,59 @@ class WhirlpoolSensorEntityDescription(SensorEntityDescription):
)


@dataclass(frozen=True, kw_only=True)
class WhirlpoolOvenCavitySensorEntityDescription(SensorEntityDescription):
"""Describes a Whirlpool oven cavity sensor entity."""

value_fn: Callable[[Oven, OvenCavity], str | int | float | None]


OVEN_CAVITY_SENSORS: tuple[WhirlpoolOvenCavitySensorEntityDescription, ...] = (
WhirlpoolOvenCavitySensorEntityDescription(
key="oven_state",
translation_key="oven_state",
device_class=SensorDeviceClass.ENUM,
options=list(OVEN_CAVITY_STATE.values()),
value_fn=lambda oven, cavity: (
OVEN_CAVITY_STATE.get(state)
if (state := oven.get_cavity_state(cavity)) is not None
else None
),
),
WhirlpoolOvenCavitySensorEntityDescription(
key="oven_cook_mode",
translation_key="oven_cook_mode",
device_class=SensorDeviceClass.ENUM,
options=list(OVEN_COOK_MODE.values()),
value_fn=lambda oven, cavity: (
OVEN_COOK_MODE.get(cook_mode)
if (cook_mode := oven.get_cook_mode(cavity)) is not None
else None
),
),
WhirlpoolOvenCavitySensorEntityDescription(
key="oven_current_temperature",
translation_key="oven_current_temperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda oven, cavity: (
temp if (temp := oven.get_temp(cavity)) != 0 else None
),
),
WhirlpoolOvenCavitySensorEntityDescription(
key="oven_target_temperature",
translation_key="oven_target_temperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda oven, cavity: (
temp if (temp := oven.get_target_temp(cavity)) != 0 else None
),
),
)


async def async_setup_entry(
hass: HomeAssistant,
config_entry: WhirlpoolConfigEntry,
Expand Down Expand Up @@ -215,12 +293,28 @@ async def async_setup_entry(
for description in WASHER_DRYER_TIME_SENSORS
]

oven_upper_cavity_sensors = [
WhirlpoolOvenCavitySensor(oven, OvenCavity.Upper, description)
for oven in appliances_manager.ovens
if oven.get_oven_cavity_exists(OvenCavity.Upper)
for description in OVEN_CAVITY_SENSORS
]

oven_lower_cavity_sensors = [
WhirlpoolOvenCavitySensor(oven, OvenCavity.Lower, description)
for oven in appliances_manager.ovens
if oven.get_oven_cavity_exists(OvenCavity.Lower)
for description in OVEN_CAVITY_SENSORS
]

async_add_entities(
[
*washer_sensors,
*washer_time_sensors,
*dryer_sensors,
*dryer_time_sensors,
*oven_upper_cavity_sensors,
*oven_lower_cavity_sensors,
]
)

Expand Down Expand Up @@ -333,3 +427,26 @@ def _is_machine_state_finished(self) -> bool:
def _is_machine_state_running(self) -> bool:
"""Return true if the machine is in a running state."""
return self._appliance.get_machine_state() is DryerMachineState.RunningMainCycle


class WhirlpoolOvenCavitySensor(WhirlpoolOvenEntity, SensorEntity):
"""A class for Whirlpool oven cavity sensors."""

def __init__(
self,
oven: Oven,
cavity: OvenCavity,
description: WhirlpoolOvenCavitySensorEntityDescription,
) -> None:
"""Initialize the oven cavity sensor."""
super().__init__(
oven, cavity, description.translation_key, f"-{description.key}"
)
self.entity_description: WhirlpoolOvenCavitySensorEntityDescription = (
description
)

@property
def native_value(self) -> StateType:
"""Return native value of sensor."""
return self.entity_description.value_fn(self._appliance, self.cavity)
Loading
Loading