1
mirror of https://github.com/home-assistant/core synced 2024-09-28 03:04:04 +02:00

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:
wilburCforce 2024-01-29 13:58:12 -06:00 committed by GitHub
parent a289ab9044
commit 39d263599e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 190 additions and 4 deletions

View File

@ -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

View File

@ -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:

View 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

View File

@ -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

View File

@ -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."
}
}
}