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
21 changes: 3 additions & 18 deletions homeassistant/components/counter/trigger.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
"""Provides triggers for counters."""

from homeassistant.const import (
CONF_MAXIMUM,
CONF_MINIMUM,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.const import CONF_MAXIMUM, CONF_MINIMUM
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers.automation import DomainSpec
from homeassistant.helpers.trigger import (
Expand Down Expand Up @@ -41,19 +36,15 @@ class CounterDecrementedTrigger(CounterBaseIntegerTrigger):
"""Trigger for when a counter is decremented."""

def is_valid_transition(self, from_state: State, to_state: State) -> bool:
"""Check if the origin state is valid and the state has changed."""
if from_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return False
"""Check that the counter value decreased."""
return int(from_state.state) > int(to_state.state)


class CounterIncrementedTrigger(CounterBaseIntegerTrigger):
"""Trigger for when a counter is incremented."""

def is_valid_transition(self, from_state: State, to_state: State) -> bool:
"""Check if the origin state is valid and the state has changed."""
if from_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return False
"""Check that the counter value increased."""
return int(from_state.state) < int(to_state.state)


Expand All @@ -62,12 +53,6 @@ class CounterValueBaseTrigger(EntityTriggerBase):

_domain_specs = {DOMAIN: DomainSpec()}

def is_valid_transition(self, from_state: State, to_state: State) -> bool:
"""Check if the origin state is valid and the state has changed."""
if from_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return False
return from_state.state != to_state.state


class CounterMaxReachedTrigger(CounterValueBaseTrigger):
"""Trigger for when a counter reaches its maximum value."""
Expand Down
6 changes: 2 additions & 4 deletions homeassistant/components/cover/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from collections.abc import Mapping

from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers.trigger import EntityTriggerBase, Trigger

Expand All @@ -28,9 +28,7 @@ def is_valid_state(self, state: State) -> bool:
return self._get_value(state) == domain_spec.target_value

def is_valid_transition(self, from_state: State, to_state: State) -> bool:
"""Check if the transition is valid for a cover state change."""
if from_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return False
"""Check that the relevant cover value changed."""
if (from_value := self._get_value(from_state)) is None:
return False
return from_value != self._get_value(to_state)
Expand Down
6 changes: 2 additions & 4 deletions homeassistant/components/doorbell/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ class DoorbellRangTrigger(StatelessEntityTriggerBase):
_domain_specs = {EVENT_DOMAIN: DomainSpec(device_class=EventDeviceClass.DOORBELL)}

def is_valid_state(self, state: State) -> bool:
"""Check if the entity is available and the event type is ring."""
return super().is_valid_state(state) and (
state.attributes.get(ATTR_EVENT_TYPE) == DoorbellEventType.RING
)
"""Check if the event type is ring."""
return state.attributes.get(ATTR_EVENT_TYPE) == DoorbellEventType.RING


TRIGGERS: dict[str, type[Trigger]] = {
Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/event/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ def __init__(self, hass: HomeAssistant, config: TriggerConfig) -> None:

def is_valid_state(self, state: State) -> bool:
"""Check if the event type matches one of the configured types."""
return super().is_valid_state(state) and (
state.attributes.get(ATTR_EVENT_TYPE) in self._event_types
)
return state.attributes.get(ATTR_EVENT_TYPE) in self._event_types


TRIGGERS: dict[str, type[Trigger]] = {
Expand Down
6 changes: 1 addition & 5 deletions homeassistant/components/media_player/trigger.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Provides triggers for media players."""

from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers.automation import DomainSpec
from homeassistant.helpers.trigger import (
Expand Down Expand Up @@ -50,10 +49,7 @@ def is_muted(self, state: State) -> bool:
)

def is_valid_transition(self, from_state: State, to_state: State) -> bool:
"""Check if the origin state is valid and the state has changed."""
if from_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return False

"""Check that the muted-state changed."""
if not self._has_volume_attributes(to_state):
return False

Expand Down
23 changes: 10 additions & 13 deletions homeassistant/components/mill/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,38 @@
from mill import Mill
from mill_local import Mill as MillLocal

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import CLOUD, CONNECTION_TYPE, DOMAIN, LOCAL
from .coordinator import MillDataUpdateCoordinator, MillHistoricDataUpdateCoordinator
from .coordinator import (
MillConfigEntry,
MillDataUpdateCoordinator,
MillHistoricDataUpdateCoordinator,
)

PLATFORMS = [Platform.CLIMATE, Platform.NUMBER, Platform.SENSOR]

__all__ = ["CLOUD", "CONNECTION_TYPE", "DOMAIN", "LOCAL"]

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up the Mill heater."""
hass.data.setdefault(DOMAIN, {LOCAL: {}, CLOUD: {}})

async def async_setup_entry(hass: HomeAssistant, entry: MillConfigEntry) -> bool:
"""Set up the Mill heater."""
if entry.data.get(CONNECTION_TYPE) == LOCAL:
mill_data_connection = MillLocal(
entry.data[CONF_IP_ADDRESS],
websession=async_get_clientsession(hass),
)
update_interval = timedelta(seconds=15)
key = entry.data[CONF_IP_ADDRESS]
conn_type = LOCAL
else:
mill_data_connection = Mill(
entry.data[CONF_USERNAME],
entry.data[CONF_PASSWORD],
websession=async_get_clientsession(hass),
)
update_interval = timedelta(seconds=30)
key = entry.data[CONF_USERNAME]
conn_type = CLOUD

historic_data_coordinator = MillHistoricDataUpdateCoordinator(
hass,
Expand All @@ -56,14 +55,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)

await data_coordinator.async_config_entry_first_refresh()
# Uses legacy hass.data[DOMAIN] pattern
# pylint: disable-next=hass-use-runtime-data
hass.data[DOMAIN][conn_type][key] = data_coordinator
entry.runtime_data = data_coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: MillConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
20 changes: 5 additions & 15 deletions homeassistant/components/mill/climate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Support for mill wifi-enabled home heaters."""
# pylint: disable=hass-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern

from typing import Any

Expand All @@ -14,14 +13,7 @@
HVACAction,
HVACMode,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_TEMPERATURE,
CONF_IP_ADDRESS,
CONF_USERNAME,
PRECISION_TENTHS,
UnitOfTemperature,
)
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, UnitOfTemperature
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
Expand All @@ -33,7 +25,6 @@
ATTR_COMFORT_TEMP,
ATTR_ROOM_NAME,
ATTR_SLEEP_TEMP,
CLOUD,
CONNECTION_TYPE,
DOMAIN,
LOCAL,
Expand All @@ -42,7 +33,7 @@
MIN_TEMP,
SERVICE_SET_ROOM_TEMP,
)
from .coordinator import MillDataUpdateCoordinator
from .coordinator import MillConfigEntry, MillDataUpdateCoordinator
from .entity import MillBaseEntity

SET_ROOM_TEMP_SCHEMA = vol.Schema(
Expand All @@ -57,17 +48,16 @@

async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: MillConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Mill climate."""
mill_data_coordinator = entry.runtime_data

if entry.data.get(CONNECTION_TYPE) == LOCAL:
mill_data_coordinator = hass.data[DOMAIN][LOCAL][entry.data[CONF_IP_ADDRESS]]
async_add_entities([LocalMillHeater(mill_data_coordinator)])
return

mill_data_coordinator = hass.data[DOMAIN][CLOUD][entry.data[CONF_USERNAME]]

entities = [
MillHeater(mill_data_coordinator, mill_device)
for mill_device in mill_data_coordinator.data.values()
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/mill/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ def __init__(
)


type MillConfigEntry = ConfigEntry[MillDataUpdateCoordinator]


class MillHistoricDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching Mill historic data."""

Expand Down
15 changes: 5 additions & 10 deletions homeassistant/components/mill/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,23 @@
from mill import Heater, MillDevice

from homeassistant.components.number import NumberDeviceClass, NumberEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_USERNAME, UnitOfPower
from homeassistant.const import UnitOfPower
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import CLOUD, CONNECTION_TYPE, DOMAIN
from .coordinator import MillDataUpdateCoordinator
from .const import CLOUD, CONNECTION_TYPE
from .coordinator import MillConfigEntry, MillDataUpdateCoordinator
from .entity import MillBaseEntity


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: MillConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Mill Number."""
if entry.data.get(CONNECTION_TYPE) == CLOUD:
# Uses legacy hass.data[DOMAIN] pattern
# pylint: disable-next=hass-use-runtime-data
mill_data_coordinator: MillDataUpdateCoordinator = hass.data[DOMAIN][CLOUD][
entry.data[CONF_USERNAME]
]
mill_data_coordinator = entry.runtime_data

async_add_entities(
MillNumber(mill_data_coordinator, mill_device)
Expand Down
16 changes: 4 additions & 12 deletions homeassistant/components/mill/sensor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Support for mill wifi-enabled home heaters."""
# pylint: disable=hass-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern

import mill

Expand All @@ -9,12 +8,9 @@
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
CONF_IP_ADDRESS,
CONF_USERNAME,
PERCENTAGE,
EntityCategory,
UnitOfEnergy,
Expand All @@ -29,19 +25,17 @@

from .const import (
BATTERY,
CLOUD,
CONNECTION_TYPE,
CONSUMPTION_TODAY,
CONSUMPTION_YEAR,
DOMAIN,
ECO2,
HUMIDITY,
LOCAL,
MANUFACTURER,
TEMPERATURE,
TVOC,
)
from .coordinator import MillDataUpdateCoordinator
from .coordinator import MillConfigEntry, MillDataUpdateCoordinator
from .entity import MillBaseEntity

HEATER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
Expand Down Expand Up @@ -146,13 +140,13 @@

async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: MillConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Mill sensor."""
if entry.data.get(CONNECTION_TYPE) == LOCAL:
mill_data_coordinator = hass.data[DOMAIN][LOCAL][entry.data[CONF_IP_ADDRESS]]
mill_data_coordinator = entry.runtime_data

if entry.data.get(CONNECTION_TYPE) == LOCAL:
async_add_entities(
LocalMillSensor(
mill_data_coordinator,
Expand All @@ -162,8 +156,6 @@ async def async_setup_entry(
)
return

mill_data_coordinator = hass.data[DOMAIN][CLOUD][entry.data[CONF_USERNAME]]

entities = [
MillSensor(
mill_data_coordinator,
Expand Down
22 changes: 4 additions & 18 deletions homeassistant/components/netatmo/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,11 @@


def get_opening_category(netatmo_device: NetatmoDevice) -> str:
"""Helper function to get opening category from Netatmo API raw data."""

# Iterate through each home in the raw data.
for home in netatmo_device.data_handler.account.raw_data["homes"]:
# Check if the modules list exists for the current home.
if "modules" in home:
# Iterate through each module to find a matching ID.
for module in home["modules"]:
if module["id"] == netatmo_device.device.entity_id:
# We found the matching device. Get its category.
if module.get("category") is not None:
return cast(str, module["category"])
raise ValueError(
f"Device {netatmo_device.device.entity_id} found, "
"but 'category' is missing in raw data."
)
"""Helper function to get opening category for doortag."""

raise ValueError(
f"Device {netatmo_device.device.entity_id} not found in Netatmo raw data."
return (
getattr(netatmo_device.device, "doortag_category", None)
or DOORTAG_CATEGORY_OTHER
)


Expand Down
7 changes: 2 additions & 5 deletions homeassistant/components/schedule/trigger.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Provides triggers for schedules."""

from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers.automation import DomainSpec
from homeassistant.helpers.trigger import (
Expand All @@ -20,10 +20,7 @@ class ScheduleBackToBackTrigger(EntityTransitionTriggerBase):
_to_states = {STATE_ON}

def is_valid_transition(self, from_state: State, to_state: State) -> bool:
"""Check if the origin state matches the expected ones."""
if from_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return False

"""Check that the origin matches and the next event changed."""
from_next_event = from_state.attributes.get(ATTR_NEXT_EVENT)
to_next_event = to_state.attributes.get(ATTR_NEXT_EVENT)

Expand Down
Loading
Loading