Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improves light support #155

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
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
143 changes: 93 additions & 50 deletions custom_components/smartir/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,16 @@
CMD_POWER_ON = "on"
CMD_POWER_OFF = "off"
CMD_NIGHTLIGHT = "night"
CMD_COLORTEMPERATURE = "colorTemperature"
CMD_BRIGHTNESS = "brightness"

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_UNIQUE_ID): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Required(CONF_DEVICE_CODE): cv.positive_int,
vol.Required(CONF_CONTROLLER_DATA): get_controller_schema(vol, cv),
vol.Optional(CONF_DELAY, default=DEFAULT_DELAY): cv.string,
vol.Optional(CONF_DELAY, default=DEFAULT_DELAY): cv.positive_float,
vol.Optional(CONF_POWER_SENSOR): cv.entity_id,
vol.Optional(
CONF_POWER_SENSOR_DELAY, default=DEFAULT_POWER_SENSOR_DELAY
Expand Down Expand Up @@ -105,13 +107,13 @@ def __init__(self, hass, config, device_data):
self._colortemps = device_data["colorTemperature"]
self._commands = device_data["commands"]

if (
if CMD_COLORTEMPERATURE in self._commands or (
CMD_COLORMODE_COLDER in self._commands
and CMD_COLORMODE_WARMER in self._commands
):
self._colortemp = self.max_color_temp_kelvin

if CMD_NIGHTLIGHT in self._commands or (
if CMD_NIGHTLIGHT in self._commands or CMD_BRIGHTNESS in self._commands or (
CMD_BRIGHTNESS_INCREASE in self._commands
and CMD_BRIGHTNESS_DECREASE in self._commands
):
Expand Down Expand Up @@ -221,36 +223,60 @@ async def async_turn_on(self, **params):
# Turn the light on if off
if self._state != STATE_ON and not self._on_by_remote:
self._state = STATE_ON
did_something = True
await self.send_command(CMD_POWER_ON)
if CMD_POWER_ON in self._commands:
did_something = True
await self.send_command(CMD_POWER_ON)
else:
if ATTR_COLOR_TEMP_KELVIN not in params:
_LOGGER.debug(
f"No power on command found, setting last color {self._colortemp}K"
)
params[ATTR_COLOR_TEMP_KELVIN] = self._colortemp
if ATTR_BRIGHTNESS not in params:
_LOGGER.debug(
f"No power on command found, setting last brightness {self._brightness}"
)
params[ATTR_BRIGHTNESS] = self._brightness

if (
ATTR_COLOR_TEMP_KELVIN in params
and ColorMode.COLOR_TEMP in self.supported_color_modes
):
did_something = True
target = params.get(ATTR_COLOR_TEMP_KELVIN)
old_color_temp = DeviceData.closest_match(self._colortemp, self._colortemps)
new_color_temp = DeviceData.closest_match(target, self._colortemps)
_LOGGER.debug(
f"Changing color temp from {self._colortemp}K step {old_color_temp} to {target}K step {new_color_temp}"
)

steps = new_color_temp - old_color_temp
did_something = True
if steps < 0:
cmd = CMD_COLORMODE_WARMER
steps = abs(steps)
else:
cmd = CMD_COLORMODE_COLDER

if steps > 0 and cmd:
# If we are heading for the highest or lowest value,
# take the opportunity to resync by issuing enough
# commands to go the full range.
if new_color_temp == len(self._colortemps) - 1 or new_color_temp == 0:
steps = len(self._colortemps)
final_color_temp = f"{self._colortemps[new_color_temp]}"
if (
CMD_COLORTEMPERATURE in self._commands
and isinstance(self._commands[CMD_COLORTEMPERATURE], dict)
and final_color_temp in self._commands[CMD_COLORTEMPERATURE]
):
_LOGGER.debug(
f"Changing color temp from {self._colortemp}K to {target}K using found remote command for {final_color_temp}K"
)
found_command = self._commands[CMD_COLORTEMPERATURE][final_color_temp]
self._colortemp = self._colortemps[new_color_temp]
await self.send_command(cmd, steps)
await self.send_remote_command(found_command)
else:
_LOGGER.debug(
f"Changing color temp from {self._colortemp}K step {old_color_temp} to {target}K step {new_color_temp}"
)
steps = new_color_temp - old_color_temp
if steps < 0:
cmd = CMD_COLORMODE_WARMER
steps = abs(steps)
else:
cmd = CMD_COLORMODE_COLDER

if steps > 0 and cmd:
# If we are heading for the highest or lowest value,
# take the opportunity to resync by issuing enough
# commands to go the full range.
if new_color_temp == len(self._colortemps) - 1 or new_color_temp == 0:
steps = len(self._colortemps)
self._colortemp = self._colortemps[new_color_temp]
await self.send_command(cmd, steps)

if ATTR_BRIGHTNESS in params and self._support_brightness:
# before checking the supported brightnesses, make a special case
Expand All @@ -262,34 +288,46 @@ async def async_turn_on(self, **params):
await self.send_command(CMD_NIGHTLIGHT)

elif self._brightnesses:
did_something = True
target = params.get(ATTR_BRIGHTNESS)
old_brightness = DeviceData.closest_match(
self._brightness, self._brightnesses
)
new_brightness = DeviceData.closest_match(target, self._brightnesses)
did_something = True
_LOGGER.debug(
f"Changing brightness from {self._brightness} step {old_brightness} to {target} step {new_brightness}"
)
steps = new_brightness - old_brightness
if steps < 0:
cmd = CMD_BRIGHTNESS_DECREASE
steps = abs(steps)
else:
cmd = CMD_BRIGHTNESS_INCREASE

if steps > 0 and cmd:
# If we are heading for the highest or lowest value,
# take the opportunity to resync by issuing enough
# commands to go the full range.
if (
new_brightness == len(self._brightnesses) - 1
or new_brightness == 0
):
steps = len(self._colortemps)
did_something = True
new_brightness = DeviceData.closest_match(target, self._brightnesses)
final_brightness = f"{self._brightnesses[new_brightness]}"
if (
CMD_BRIGHTNESS in self._commands
and isinstance(self._commands[CMD_BRIGHTNESS], dict)
and final_brightness in self._commands[CMD_BRIGHTNESS]
):
_LOGGER.debug(
f"Changing brightness from {self._brightness} to {target} using found remote command for {final_brightness}"
)
found_command = self._commands[CMD_BRIGHTNESS][final_brightness]
self._brightness = self._brightnesses[new_brightness]
await self.send_command(cmd, steps)
await self.send_remote_command(found_command)
else:
_LOGGER.debug(
f"Changing brightness from {self._brightness} step {old_brightness} to {target} step {new_brightness}"
)
steps = new_brightness - old_brightness
if steps < 0:
cmd = CMD_BRIGHTNESS_DECREASE
steps = abs(steps)
else:
cmd = CMD_BRIGHTNESS_INCREASE

if steps > 0 and cmd:
# If we are heading for the highest or lowest value,
# take the opportunity to resync by issuing enough
# commands to go the full range.
if (
new_brightness == len(self._brightnesses) - 1
or new_brightness == 0
):
steps = len(self._brightnesses)
self._brightness = self._brightnesses[new_brightness]
await self.send_command(cmd, steps)

# If we did nothing above, and the light is not detected as on
# already issue the on command, even though we think the light
Expand All @@ -304,9 +342,10 @@ async def async_turn_on(self, **params):
self.async_write_ha_state()

async def async_turn_off(self):
self._state = STATE_OFF
await self.send_command(CMD_POWER_OFF)
self.async_write_ha_state()
if self._state != STATE_OFF:
self._state = STATE_OFF
await self.send_command(CMD_POWER_OFF)
self.async_write_ha_state()

async def async_toggle(self):
await (self.async_turn_on() if not self.is_on else self.async_turn_off())
Expand All @@ -317,11 +356,15 @@ async def send_command(self, cmd, count=1):
return
_LOGGER.debug(f"Sending {cmd} remote command {count} times.")
remote_cmd = self._commands.get(cmd)
await self.send_remote_command(remote_cmd, count)

async def send_remote_command(self, remote_cmd, count=1):
async with self._temp_lock:
self._on_by_remote = False
try:
for _ in range(count):
await self._controller.send(remote_cmd)
await asyncio.sleep(self._delay)
except Exception as e:
_LOGGER.exception(e)

Expand Down
100 changes: 100 additions & 0 deletions docs/CODES_SYNTAX.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,106 @@ These are command to set controlled device into desired work state. Due to the n
},
```

## Light Speficic

### Light declaration part

```yaml:
"brightness": [26, 51, 77, 102, 128, 153, 179, 204, 230, 255],
"colorTemperature": [
2700, 4600, 6500
],
```

| json attribute | mandatory | type | description |
| ----------------- | :-------: | :----------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `brightness` | `no` | `array of int` | List of supported brightness levels for the light in the range of 0 to 255. |
| `colorTemperature` | `yes` | `array of int` | List of supported color temperatures for the light in Kelvin (K). |

### Light commands

#### Light `off` commands

The off command specifies the IR code used to turn off the light. This command is necessary for any basic light control setup.

```yaml:
"commands": {
"off": "JgCSAAABKHQKDGgHtSq.............",
```

#### Light `on` commands

The on command is used to power on the device. In some cases, a dedicated on command is not required if the device can be turned on by setting a brightness or color temperature value.
Since the device state is only assumed without a power sensor, it is highly recommended to use the power sensor feature in this case.

```yaml:
"commands": {
"on": "JgCSAAABKHQKDGgHtSq.............",
```

#### Light `night` commands

If a night command is specified, there is a special case when the requested brightness is 1, using the defined command:

```yaml:
"commands": {
"night": "JgCSAAABKZIXEBcRFz.............",
```

#### Light brightness control

The brightness control functionality enables dynamic adjustments to the light’s intensity. This can be achieved through general brighten and dim commands or by specifying individual commands for each supported brightness level.

##### Light brighten and dim commands

The brighten and dim commands incrementally adjust the brightness up or down, respectively.

```yaml:
"commands": {
"brighten": "JgCSAAABKZIXEBcRFz.............",
"dim": "JgCSAAABKZIXEBcRFz.............",
```

##### Light brightness commands

Alternatively, each supported brightness level can have a dedicated command.

```yaml:
"commands": {
"brightness": {
"26": "JgCSAAABKZIXEBcRFz.............",
"51": "JgCSAAABKZIXEBcRFz.............",
...
}
```

#### Light color temperature control

Color temperature control allows users to adjust the warmth or coolness of the light. Similar to brightness, this can be managed with general commands or specific values.

##### Light colder and warmer commands

The colder and warmer commands provide a general way to adjust the color temperature incrementally.

```yaml:
"commands": {
"colder": "JgCSAAABKZIXEBcRFz.............",
"warmer": "JgCSAAABKZIXEBcRFz.............",
```

##### Light colorTemperature commands

Alternatively, commands can be defined for each supported color temperature value.

```yaml:
"commands": {
"colorTemperature": {
"2700": "JgCSAAABKZIXEBcRFz.............",
"4600": "JgCSAAABKZIXEBcRFz.............",
"6500": "JgCSAAABKZIXEBcRFz.............",
}
```

## Fan Speficic

TBD
Expand Down
Loading