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
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ homeassistant.components.transmission.*
homeassistant.components.trend.*
homeassistant.components.trmnl.*
homeassistant.components.tts.*
homeassistant.components.tween_light_ir.*
homeassistant.components.twentemilieu.*
homeassistant.components.unifi.*
homeassistant.components.unifi_access.*
Expand Down
2 changes: 2 additions & 0 deletions CODEOWNERS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions homeassistant/components/tween_light_ir/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""The Tween Light Infrared integration."""

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant

PLATFORMS: list[Platform] = [Platform.LIGHT]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Tween Light Infrared from a config entry."""
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
92 changes: 92 additions & 0 deletions homeassistant/components/tween_light_ir/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Config flow for the Tween Light Infrared integration."""

from typing import Any

import voluptuous as vol

from homeassistant.components.infrared import (
DOMAIN as INFRARED_DOMAIN,
async_get_emitters,
async_get_receivers,
)
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.helpers.selector import (
EntitySelector,
EntitySelectorConfig,
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
)

from .const import (
CONF_DEVICE_TYPE,
CONF_INFRARED_ENTITY_ID,
CONF_INFRARED_RECEIVER_ENTITY_ID,
DEVICE_TYPE_NAMES,
DOMAIN,
TweenLightIrDeviceType,
)


class TweenLightIrConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Tween Light Infrared."""

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
errors: dict[str, str] = {}
emitter_entity_ids = async_get_emitters(self.hass)
receiver_entity_ids = async_get_receivers(self.hass)
if not emitter_entity_ids and not receiver_entity_ids:
return self.async_abort(reason="no_infrared_entities")

if user_input is not None:
if user_input.get(CONF_INFRARED_ENTITY_ID) or user_input.get(
CONF_INFRARED_RECEIVER_ENTITY_ID
):
Comment on lines +45 to +47
self._async_abort_entries_match(
{
CONF_DEVICE_TYPE: user_input[CONF_DEVICE_TYPE],
CONF_INFRARED_ENTITY_ID: user_input.get(
CONF_INFRARED_ENTITY_ID
),
}
)
Comment on lines +48 to +55
Comment on lines +45 to +55
return self.async_create_entry(
title=DEVICE_TYPE_NAMES[user_input[CONF_DEVICE_TYPE]],
data=user_input,
)
Comment on lines +56 to +59
Comment on lines +56 to +59
Comment on lines +57 to +59

errors["base"] = "missing_infrared_entity"
Comment on lines +41 to +61

return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_DEVICE_TYPE): SelectSelector(
SelectSelectorConfig(
options=[
device_type.value
for device_type in TweenLightIrDeviceType
],
translation_key=CONF_DEVICE_TYPE,
mode=SelectSelectorMode.DROPDOWN,
)
),
vol.Optional(CONF_INFRARED_ENTITY_ID): EntitySelector(
EntitySelectorConfig(
domain=INFRARED_DOMAIN,
include_entities=emitter_entity_ids,
)
),
vol.Optional(CONF_INFRARED_RECEIVER_ENTITY_ID): EntitySelector(
EntitySelectorConfig(
domain=INFRARED_DOMAIN,
include_entities=receiver_entity_ids,
)
),
}
),
errors=errors,
)
19 changes: 19 additions & 0 deletions homeassistant/components/tween_light_ir/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Constants for the Tween Light Infrared integration."""

from enum import StrEnum

DOMAIN = "tween_light_ir"
CONF_INFRARED_ENTITY_ID = "infrared_entity_id"
CONF_INFRARED_RECEIVER_ENTITY_ID = "infrared_receiver_entity_id"
CONF_DEVICE_TYPE = "device_type"


class TweenLightIrDeviceType(StrEnum):
"""Tween Light Infrared device types."""

LED_STRIP = "led_strip"


DEVICE_TYPE_NAMES: dict[TweenLightIrDeviceType, str] = {
TweenLightIrDeviceType.LED_STRIP: "LED Strip",
}
26 changes: 26 additions & 0 deletions homeassistant/components/tween_light_ir/entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Common entity for Tween Light Infrared integration."""

from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity

from .const import CONF_DEVICE_TYPE, DEVICE_TYPE_NAMES, DOMAIN


class TweenLightIrEntity(Entity):
"""Tween Light Infrared base entity providing common device info."""

_attr_has_entity_name = True

def __init__(self, entry: ConfigEntry, unique_id_suffix: str | None = None) -> None:
"""Initialize entity."""
self._attr_unique_id = (
f"{entry.entry_id}_{unique_id_suffix}"
if unique_id_suffix is not None
else entry.entry_id
)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, entry.entry_id)},
model=DEVICE_TYPE_NAMES[entry.data[CONF_DEVICE_TYPE]],
manufacturer="Tween Light",
)
Comment on lines +22 to +26
38 changes: 38 additions & 0 deletions homeassistant/components/tween_light_ir/icons.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"entity": {
"light": {
"led_strip": {
"default": "mdi:led-strip-variant",
"state": {
"off": "mdi:led-strip-variant-off"
},
"state_attributes": {
"effect": {
"state": {
"blue": "mdi:palette",
"cyan": "mdi:palette",
"dark_cyan": "mdi:palette",
"fade": "mdi:gradient-horizontal",
"flash": "mdi:flash",
"green": "mdi:palette",
"light_green": "mdi:palette",
"orange": "mdi:palette",
"orange_red": "mdi:palette",
"plum": "mdi:palette",
"purple": "mdi:palette",
"rebecca_purple": "mdi:palette",
"red": "mdi:palette",
"sky_blue": "mdi:palette",
"smooth": "mdi:looks",
"strobe": "mdi:light-flood-down",
"tomato": "mdi:palette",
"turquoise": "mdi:palette",
"white": "mdi:palette",
"yellow": "mdi:palette"
}
}
}
}
}
}
}
92 changes: 92 additions & 0 deletions homeassistant/components/tween_light_ir/light.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Light platform for Tween Light Infrared integration."""

from typing import Any

from infrared_protocols.codes.tween_light.led_strip import TweenLightLEDStripCode

from homeassistant.components.infrared import InfraredEmitterConsumerEntity
from homeassistant.components.light import (
ATTR_EFFECT,
ColorMode,
LightEntity,
LightEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import CONF_DEVICE_TYPE, CONF_INFRARED_ENTITY_ID, TweenLightIrDeviceType
from .entity import TweenLightIrEntity

PARALLEL_UPDATES = 1


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up platform from config entry."""
if not (infrared_entity_id := entry.data.get(CONF_INFRARED_ENTITY_ID)):
return
Comment on lines +30 to +31

device_type = entry.data[CONF_DEVICE_TYPE]
if device_type == TweenLightIrDeviceType.LED_STRIP:
async_add_entities([TweenLightIrLightEntity(entry, infrared_entity_id)])
Comment on lines +29 to +35


class TweenLightIrLightEntity(
TweenLightIrEntity, InfraredEmitterConsumerEntity, LightEntity
):
"""Represents a Tween Light Infrared light entity."""

_attr_assumed_state = True
_attr_color_mode = ColorMode.ONOFF
_attr_effect_list = [
"flash",
"strobe",
"fade",
"smooth",
"red",
"green",
"blue",
"white",
"tomato",
"light_green",
"sky_blue",
"orange_red",
"cyan",
"rebecca_purple",
"orange",
"turquoise",
"purple",
"yellow",
"dark_cyan",
"plum",
]
_attr_name = None
_attr_supported_color_modes = {ColorMode.ONOFF}
_attr_supported_features = LightEntityFeature.EFFECT
_attr_translation_key = "led_strip"

def __init__(self, entry: ConfigEntry, infrared_entity_id: str) -> None:
"""Initialize the entity."""
super().__init__(entry)
self._infrared_emitter_entity_id = infrared_entity_id

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn device on."""
command = TweenLightLEDStripCode.ON.to_command()
self._attr_is_on = True
if ATTR_EFFECT in kwargs and kwargs[ATTR_EFFECT] in self._attr_effect_list:
effect: str = kwargs[ATTR_EFFECT]
command = TweenLightLEDStripCode[effect.upper()].to_command()

await self._send_command(command)
self.async_write_ha_state()
Comment on lines +77 to +86

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
self._attr_is_on = False
await self._send_command(TweenLightLEDStripCode.OFF.to_command())
self.async_write_ha_state()
10 changes: 10 additions & 0 deletions homeassistant/components/tween_light_ir/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"domain": "tween_light_ir",
"name": "Tween Light Infrared",
"codeowners": ["@tr4nt0r"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/tween_light_ir",
"integration_type": "device",
"iot_class": "assumed_state",
"quality_scale": "bronze"
}
Loading
Loading