Add lutron fan entity (#107402)
* add support for fan entity * removed unused variables * removed preset leftovers - not needed * added deprecation for fans * Update __init__.py * fix typing * initial updates based on review * updated to search on unique ID instead of entity ID. * updates for nits * nits updates * updates for new callback * removed async per nits * wrapped comments into shorter lines * Add comment comma --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
a289ab9044
commit
39d263599e
5 changed files with 190 additions and 4 deletions
|
@ -718,6 +718,7 @@ omit =
|
|||
homeassistant/components/lutron/binary_sensor.py
|
||||
homeassistant/components/lutron/cover.py
|
||||
homeassistant/components/lutron/entity.py
|
||||
homeassistant/components/lutron/fan.py
|
||||
homeassistant/components/lutron/light.py
|
||||
homeassistant/components/lutron/switch.py
|
||||
homeassistant/components/lutron_caseta/__init__.py
|
||||
|
|
|
@ -27,6 +27,7 @@ from .const import DOMAIN
|
|||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.COVER,
|
||||
Platform.FAN,
|
||||
Platform.LIGHT,
|
||||
Platform.SCENE,
|
||||
Platform.SWITCH,
|
||||
|
@ -167,6 +168,7 @@ class LutronData:
|
|||
binary_sensors: list[tuple[str, OccupancyGroup]]
|
||||
buttons: list[LutronButton]
|
||||
covers: list[tuple[str, Output]]
|
||||
fans: list[tuple[str, Output]]
|
||||
lights: list[tuple[str, Output]]
|
||||
scenes: list[tuple[str, Keypad, Button, Led]]
|
||||
switches: list[tuple[str, Output]]
|
||||
|
@ -189,6 +191,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
|||
binary_sensors=[],
|
||||
buttons=[],
|
||||
covers=[],
|
||||
fans=[],
|
||||
lights=[],
|
||||
scenes=[],
|
||||
switches=[],
|
||||
|
@ -201,6 +204,10 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
|||
_LOGGER.debug("Working on output %s", output.type)
|
||||
if output.type == "SYSTEM_SHADE":
|
||||
entry_data.covers.append((area.name, output))
|
||||
elif output.type == "CEILING_FAN_TYPE":
|
||||
entry_data.fans.append((area.name, output))
|
||||
# Deprecated, should be removed in 2024.8
|
||||
entry_data.lights.append((area.name, output))
|
||||
elif output.is_dimmable:
|
||||
entry_data.lights.append((area.name, output))
|
||||
else:
|
||||
|
|
89
homeassistant/components/lutron/fan.py
Normal file
89
homeassistant/components/lutron/fan.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
"""Lutron fan platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from pylutron import Output
|
||||
|
||||
from homeassistant.components.fan import FanEntity, FanEntityFeature
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import DOMAIN, LutronData
|
||||
from .entity import LutronDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Lutron fan platform.
|
||||
|
||||
Adds fan controls from the Main Repeater associated with the config_entry as
|
||||
fan entities.
|
||||
"""
|
||||
entry_data: LutronData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
async_add_entities(
|
||||
[
|
||||
LutronFan(area_name, device, entry_data.client)
|
||||
for area_name, device in entry_data.fans
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
class LutronFan(LutronDevice, FanEntity):
|
||||
"""Representation of a Lutron fan."""
|
||||
|
||||
_attr_name = None
|
||||
_attr_should_poll = False
|
||||
_attr_speed_count = 3
|
||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
||||
_lutron_device: Output
|
||||
_prev_percentage: int | None = None
|
||||
|
||||
def set_percentage(self, percentage: int) -> None:
|
||||
"""Set the speed of the fan, as a percentage."""
|
||||
if percentage > 0:
|
||||
self._prev_percentage = percentage
|
||||
self._lutron_device.level = percentage
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def turn_on(
|
||||
self,
|
||||
percentage: int | None = None,
|
||||
preset_mode: str | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Turn the fan on."""
|
||||
new_percentage: int | None = None
|
||||
|
||||
if percentage is not None:
|
||||
new_percentage = percentage
|
||||
elif not self._prev_percentage:
|
||||
# Default to medium speed
|
||||
new_percentage = 67
|
||||
else:
|
||||
new_percentage = self._prev_percentage
|
||||
self.set_percentage(new_percentage)
|
||||
|
||||
def turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the fan off."""
|
||||
self.set_percentage(0)
|
||||
|
||||
def _request_state(self) -> None:
|
||||
"""Request the state from the device."""
|
||||
self._lutron_device.level # pylint: disable=pointless-statement
|
||||
|
||||
def _update_attrs(self) -> None:
|
||||
"""Update the state attributes."""
|
||||
level = self._lutron_device.last_level()
|
||||
self._attr_is_on = level > 0
|
||||
self._attr_percentage = level
|
||||
if self._prev_percentage is None or level != 0:
|
||||
self._prev_percentage = level
|
|
@ -2,18 +2,30 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from pylutron import Output
|
||||
|
||||
from homeassistant.components.automation import automations_with_entity
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
|
||||
from homeassistant.components.script import scripts_with_entity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.issue_registry import (
|
||||
IssueSeverity,
|
||||
async_create_issue,
|
||||
create_issue,
|
||||
)
|
||||
|
||||
from . import DOMAIN, LutronData
|
||||
from .entity import LutronDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
|
@ -25,12 +37,50 @@ async def async_setup_entry(
|
|||
Adds dimmers from the Main Repeater associated with the config_entry as
|
||||
light entities.
|
||||
"""
|
||||
ent_reg = er.async_get(hass)
|
||||
entry_data: LutronData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
lights = []
|
||||
|
||||
for area_name, device in entry_data.lights:
|
||||
if device.type == "CEILING_FAN_TYPE2":
|
||||
# If this is a fan, check to see if this entity already exists.
|
||||
# If not, do not create a new one.
|
||||
entity_id = ent_reg.async_get_entity_id(
|
||||
Platform.LIGHT,
|
||||
DOMAIN,
|
||||
f"{entry_data.client.guid}_{device.uuid}",
|
||||
)
|
||||
if entity_id:
|
||||
entity_entry = ent_reg.async_get(entity_id)
|
||||
assert entity_entry
|
||||
if entity_entry.disabled:
|
||||
# If the entity exists and is disabled then we want to remove
|
||||
# the entity so that the user is using the new fan entity instead.
|
||||
ent_reg.async_remove(entity_id)
|
||||
else:
|
||||
lights.append(LutronLight(area_name, device, entry_data.client))
|
||||
entity_automations = automations_with_entity(hass, entity_id)
|
||||
entity_scripts = scripts_with_entity(hass, entity_id)
|
||||
for item in entity_automations + entity_scripts:
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"deprecated_light_fan_{entity_id}_{item}",
|
||||
breaks_in_ha_version="2024.8.0",
|
||||
is_fixable=True,
|
||||
is_persistent=True,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_light_fan_entity",
|
||||
translation_placeholders={
|
||||
"entity": entity_id,
|
||||
"info": item,
|
||||
},
|
||||
)
|
||||
else:
|
||||
lights.append(LutronLight(area_name, device, entry_data.client))
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
LutronLight(area_name, device, entry_data.client)
|
||||
for area_name, device in entry_data.lights
|
||||
],
|
||||
lights,
|
||||
True,
|
||||
)
|
||||
|
||||
|
@ -54,8 +104,24 @@ class LutronLight(LutronDevice, LightEntity):
|
|||
_prev_brightness: int | None = None
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, area_name, lutron_device, controller) -> None:
|
||||
"""Initialize the light."""
|
||||
super().__init__(area_name, lutron_device, controller)
|
||||
self._is_fan = lutron_device.type == "CEILING_FAN_TYPE"
|
||||
|
||||
def turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the light on."""
|
||||
if self._is_fan:
|
||||
create_issue(
|
||||
self.hass,
|
||||
DOMAIN,
|
||||
"deprecated_light_fan_on",
|
||||
breaks_in_ha_version="2024.8.0",
|
||||
is_fixable=True,
|
||||
is_persistent=True,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_light_fan_on",
|
||||
)
|
||||
if ATTR_BRIGHTNESS in kwargs and self._lutron_device.is_dimmable:
|
||||
brightness = kwargs[ATTR_BRIGHTNESS]
|
||||
elif self._prev_brightness == 0:
|
||||
|
@ -67,6 +133,17 @@ class LutronLight(LutronDevice, LightEntity):
|
|||
|
||||
def turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the light off."""
|
||||
if self._is_fan:
|
||||
create_issue(
|
||||
self.hass,
|
||||
DOMAIN,
|
||||
"deprecated_light_fan_off",
|
||||
breaks_in_ha_version="2024.8.0",
|
||||
is_fixable=True,
|
||||
is_persistent=True,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_light_fan_off",
|
||||
)
|
||||
self._lutron_device.level = 0
|
||||
|
||||
@property
|
||||
|
|
|
@ -30,6 +30,18 @@
|
|||
"deprecated_yaml_import_issue_unknown": {
|
||||
"title": "The Lutron YAML configuration import request failed due to an unknown error",
|
||||
"description": "Configuring Lutron using YAML is being removed but there was an unknown error while importing your existing configuration.\nSetup will not proceed.\n\nThe specific error can be found in the logs. The most likely cause is a networking error or the Main Repeater is down or has an invalid configuration.\n\nVerify that your Lutron system is operating correctly and restart Home Assistant to attempt the import again.\n\nAlternatively, you may remove the Lutron configuration from your YAML configuration entirely, restart Home Assistant, and add the Lutron integration manually."
|
||||
},
|
||||
"deprecated_light_fan_entity": {
|
||||
"title": "Detected Lutron fan entity created as a light",
|
||||
"description": "Fan entities have been added to the Lutron integration.\nWe detected that entity `{entity}` is being used in `{info}`\n\nWe have created a new fan entity and you should migrate `{info}` to use this new entity.\n\nWhen you are done migrating `{info}` and are ready to have the deprecated light entity removed, disable the entity and restart Home Assistant."
|
||||
},
|
||||
"deprecated_light_fan_on": {
|
||||
"title": "The Lutron integration deprecated fan turned on",
|
||||
"description": "Fan entities have been added to the Lutron integration.\nPreviously fans were created as lights; this behavior is now deprecated.\n\nYour configuration just turned on a fan created as a light. You should migrate your scenes and automations to use the new fan entity.\n\nWhen you are done migrating your automations and are ready to have the deprecated light entity removed, disable the entity and restart Home Assistant.\n\nAn issue will be created each time the incorrect entity is used to remind you to migrate."
|
||||
},
|
||||
"deprecated_light_fan_off": {
|
||||
"title": "The Lutron integration deprecated fan turned off",
|
||||
"description": "Fan entities have been added to the Lutron integration.\nPreviously fans were created as lights; this behavior is now deprecated.\n\nYour configuration just turned off a fan created as a light. You should migrate your scenes and automations to use the new fan entity.\n\nWhen you are done migrating your automations and are ready to have the deprecated light entity removed, disable the entity and restart Home Assistant.\n\nAn issue will be created each time the incorrect entity is used to remind you to migrate."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue