Add transition to LiteJet (#47657)

This commit is contained in:
Jon Caruana 2021-07-24 03:43:10 -07:00 committed by GitHub
parent 0f78004ede
commit 72a3860361
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 163 additions and 18 deletions

View File

@ -10,13 +10,44 @@ import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_PORT
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
import homeassistant.helpers.config_validation as cv
from .const import DOMAIN
from .const import CONF_DEFAULT_TRANSITION, DOMAIN
_LOGGER = logging.getLogger(__name__)
class LiteJetOptionsFlow(config_entries.OptionsFlow):
"""Handle LiteJet options."""
def __init__(self, config_entry):
"""Initialize LiteJet options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> dict[str, Any]:
"""Manage LiteJet options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_DEFAULT_TRANSITION,
default=self.config_entry.options.get(
CONF_DEFAULT_TRANSITION, 0
),
): cv.positive_int,
}
),
)
class LiteJetConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""LiteJet config flow."""
@ -54,3 +85,9 @@ class LiteJetConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_import(self, import_data):
"""Import litejet config from configuration.yaml."""
return self.async_create_entry(title=import_data[CONF_PORT], data=import_data)
@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Get the options flow for this handler."""
return LiteJetOptionsFlow(config_entry)

View File

@ -6,3 +6,5 @@ CONF_EXCLUDE_NAMES = "exclude_names"
CONF_INCLUDE_SWITCHES = "include_switches"
PLATFORMS = ["light", "switch", "scene"]
CONF_DEFAULT_TRANSITION = "default_transition"

View File

@ -3,11 +3,13 @@ import logging
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_TRANSITION,
SUPPORT_BRIGHTNESS,
SUPPORT_TRANSITION,
LightEntity,
)
from .const import DOMAIN
from .const import CONF_DEFAULT_TRANSITION, DOMAIN
_LOGGER = logging.getLogger(__name__)
@ -23,7 +25,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
entities = []
for i in system.loads():
name = system.get_load_name(i)
entities.append(LiteJetLight(config_entry.entry_id, system, i, name))
entities.append(LiteJetLight(config_entry, system, i, name))
return entities
async_add_entities(await hass.async_add_executor_job(get_entities, system), True)
@ -32,9 +34,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class LiteJetLight(LightEntity):
"""Representation of a single LiteJet light."""
def __init__(self, entry_id, lj, i, name):
def __init__(self, config_entry, lj, i, name):
"""Initialize a LiteJet light."""
self._entry_id = entry_id
self._config_entry = config_entry
self._lj = lj
self._index = i
self._brightness = 0
@ -57,7 +59,7 @@ class LiteJetLight(LightEntity):
@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_BRIGHTNESS
return SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION
@property
def name(self):
@ -67,7 +69,7 @@ class LiteJetLight(LightEntity):
@property
def unique_id(self):
"""Return a unique identifier for this light."""
return f"{self._entry_id}_{self._index}"
return f"{self._config_entry.entry_id}_{self._index}"
@property
def brightness(self):
@ -91,16 +93,33 @@ class LiteJetLight(LightEntity):
def turn_on(self, **kwargs):
"""Turn on the light."""
if ATTR_BRIGHTNESS in kwargs:
brightness = int(kwargs[ATTR_BRIGHTNESS] / 255 * 99)
self._lj.activate_load_at(self._index, brightness, 0)
else:
# If neither attribute is specified then the simple activate load
# LiteJet API will use the per-light default brightness and
# transition values programmed in the LiteJet system.
if ATTR_BRIGHTNESS not in kwargs and ATTR_TRANSITION not in kwargs:
self._lj.activate_load(self._index)
return
# If either attribute is specified then Home Assistant must
# control both values.
default_transition = self._config_entry.options.get(CONF_DEFAULT_TRANSITION, 0)
transition = kwargs.get(ATTR_TRANSITION, default_transition)
brightness = int(kwargs.get(ATTR_BRIGHTNESS, 255) / 255 * 99)
self._lj.activate_load_at(self._index, brightness, int(transition))
def turn_off(self, **kwargs):
"""Turn off the light."""
if ATTR_TRANSITION in kwargs:
self._lj.activate_load_at(self._index, 0, kwargs[ATTR_TRANSITION])
return
# If transition attribute is not specified then the simple
# deactivate load LiteJet API will use the per-light default
# transition value programmed in the LiteJet system.
self._lj.deactivate_load(self._index)
def update(self):
"""Retrieve the light's brightness from the LiteJet system."""
self._brightness = self._lj.get_load_level(self._index) / 99 * 255
self._brightness = int(self._lj.get_load_level(self._index) / 99 * 255)

View File

@ -15,5 +15,15 @@
"error": {
"open_failed": "Cannot open the specified serial port."
}
},
"options": {
"step": {
"init": {
"title": "Configure LiteJet",
"data": {
"default_transition": "Default Transition (seconds)"
}
}
}
}
}

View File

@ -15,5 +15,15 @@
"title": "Connect To LiteJet"
}
}
},
"options": {
"step": {
"init": {
"data": {
"default_transition": "Default Transition (seconds)"
},
"title": "Configure LiteJet"
}
}
}
}

View File

@ -3,8 +3,8 @@ from unittest.mock import patch
from serial import SerialException
from homeassistant import config_entries
from homeassistant.components.litejet.const import DOMAIN
from homeassistant import config_entries, data_entry_flow
from homeassistant.components.litejet.const import CONF_DEFAULT_TRANSITION, DOMAIN
from homeassistant.const import CONF_PORT
from tests.common import MockConfigEntry
@ -76,3 +76,22 @@ async def test_import_step(hass):
assert result["type"] == "create_entry"
assert result["title"] == test_data[CONF_PORT]
assert result["data"] == test_data
async def test_options(hass):
"""Test updating options."""
entry = MockConfigEntry(domain=DOMAIN, data={CONF_PORT: "/dev/test"})
entry.add_to_hass(hass)
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={CONF_DEFAULT_TRANSITION: 12},
)
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["data"] == {CONF_DEFAULT_TRANSITION: 12}

View File

@ -2,7 +2,8 @@
import logging
from homeassistant.components import light
from homeassistant.components.light import ATTR_BRIGHTNESS
from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_TRANSITION
from homeassistant.components.litejet.const import CONF_DEFAULT_TRANSITION
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
from . import async_init_integration
@ -33,6 +34,55 @@ async def test_on_brightness(hass, mock_litejet):
mock_litejet.activate_load_at.assert_called_with(ENTITY_LIGHT_NUMBER, 39, 0)
async def test_default_transition(hass, mock_litejet):
"""Test turning the light on with the default transition option."""
entry = await async_init_integration(hass)
hass.config_entries.async_update_entry(entry, options={CONF_DEFAULT_TRANSITION: 12})
await hass.async_block_till_done()
assert hass.states.get(ENTITY_LIGHT).state == "off"
assert hass.states.get(ENTITY_OTHER_LIGHT).state == "off"
assert not light.is_on(hass, ENTITY_LIGHT)
await hass.services.async_call(
light.DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_BRIGHTNESS: 102},
blocking=True,
)
mock_litejet.activate_load_at.assert_called_with(ENTITY_LIGHT_NUMBER, 39, 12)
async def test_transition(hass, mock_litejet):
"""Test turning the light on with transition."""
await async_init_integration(hass)
assert hass.states.get(ENTITY_LIGHT).state == "off"
assert hass.states.get(ENTITY_OTHER_LIGHT).state == "off"
assert not light.is_on(hass, ENTITY_LIGHT)
# On
await hass.services.async_call(
light.DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_TRANSITION: 5},
blocking=True,
)
mock_litejet.activate_load_at.assert_called_with(ENTITY_LIGHT_NUMBER, 99, 5)
# Off
await hass.services.async_call(
light.DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_TRANSITION: 5},
blocking=True,
)
mock_litejet.activate_load_at.assert_called_with(ENTITY_LIGHT_NUMBER, 0, 5)
async def test_on_off(hass, mock_litejet):
"""Test turning the light on and off."""
await async_init_integration(hass)
@ -91,9 +141,7 @@ async def test_activated_event(hass, mock_litejet):
assert light.is_on(hass, ENTITY_OTHER_LIGHT)
assert hass.states.get(ENTITY_LIGHT).state == "on"
assert hass.states.get(ENTITY_OTHER_LIGHT).state == "on"
assert (
int(hass.states.get(ENTITY_OTHER_LIGHT).attributes.get(ATTR_BRIGHTNESS)) == 103
)
assert hass.states.get(ENTITY_OTHER_LIGHT).attributes.get(ATTR_BRIGHTNESS) == 103
async def test_deactivated_event(hass, mock_litejet):