python-kasa/kasa/cli/light.py

222 lines
7.4 KiB
Python
Raw Normal View History

"""Module for cli light control commands."""
import asyncclick as click
from kasa import (
Device,
Module,
)
from kasa.iot import (
IotBulb,
)
from .common import echo, error, pass_dev_or_child
@click.group()
@pass_dev_or_child
2024-11-10 18:55:13 +00:00
def light(dev) -> None:
"""Commands to control light settings."""
@light.command()
@click.argument("brightness", type=click.IntRange(0, 100), default=None, required=False)
@click.option("--transition", type=int, required=False)
@pass_dev_or_child
async def brightness(dev: Device, brightness: int, transition: int):
"""Get or set brightness."""
if not (light := dev.modules.get(Module.Light)) or not light.has_feature(
"brightness"
):
error("This device does not support brightness.")
return
if brightness is None:
echo(f"Brightness: {light.brightness}")
return light.brightness
else:
echo(f"Setting brightness to {brightness}")
return await light.set_brightness(brightness, transition=transition)
@light.command()
@click.argument(
"temperature", type=click.IntRange(2500, 9000), default=None, required=False
)
@click.option("--transition", type=int, required=False)
@pass_dev_or_child
async def temperature(dev: Device, temperature: int, transition: int):
"""Get or set color temperature."""
if not (light := dev.modules.get(Module.Light)) or not (
color_temp_feat := light.get_feature("color_temp")
):
error("Device does not support color temperature")
return
if temperature is None:
echo(f"Color temperature: {light.color_temp}")
valid_temperature_range = color_temp_feat.range
if valid_temperature_range != (0, 0):
echo("(min: {}, max: {})".format(*valid_temperature_range))
else:
echo(
"Temperature range unknown, please open a github issue"
f" or a pull request for model '{dev.model}'"
)
return color_temp_feat.range
else:
echo(f"Setting color temperature to {temperature}")
return await light.set_color_temp(temperature, transition=transition)
@light.command()
@click.argument("effect", type=click.STRING, default=None, required=False)
@click.pass_context
@pass_dev_or_child
async def effect(dev: Device, ctx, effect):
"""Set an effect."""
if not (light_effect := dev.modules.get(Module.LightEffect)):
error("Device does not support effects")
return
if effect is None:
echo(
f"Light effect: {light_effect.effect}\n"
+ f"Available Effects: {light_effect.effect_list}"
)
return light_effect.effect
if effect not in light_effect.effect_list:
raise click.BadArgumentUsage(
f"Effect must be one of: {light_effect.effect_list}", ctx
)
echo(f"Setting Effect: {effect}")
return await light_effect.set_effect(effect)
@light.command()
@click.argument("h", type=click.IntRange(0, 360), default=None, required=False)
@click.argument("s", type=click.IntRange(0, 100), default=None, required=False)
@click.argument("v", type=click.IntRange(0, 100), default=None, required=False)
@click.option("--transition", type=int, required=False)
@click.pass_context
@pass_dev_or_child
async def hsv(dev: Device, ctx, h, s, v, transition):
"""Get or set color in HSV."""
if not (light := dev.modules.get(Module.Light)) or not light.has_feature("hsv"):
error("Device does not support colors")
return
if h is None and s is None and v is None:
echo(f"Current HSV: {light.hsv}")
return light.hsv
elif s is None or v is None:
raise click.BadArgumentUsage("Setting a color requires 3 values.", ctx)
else:
echo(f"Setting HSV: {h} {s} {v}")
return await light.set_hsv(h, s, v, transition=transition)
@light.group(invoke_without_command=True)
@pass_dev_or_child
@click.pass_context
async def presets(ctx, dev):
"""List and modify bulb setting presets."""
if ctx.invoked_subcommand is None:
return await ctx.invoke(presets_list)
@presets.command(name="list")
@pass_dev_or_child
def presets_list(dev: Device):
"""List presets."""
if not (light_preset := dev.modules.get(Module.LightPreset)):
error("Device does not support light presets")
return
for idx, preset in enumerate(light_preset.preset_states_list):
echo(
f"[{idx}] Hue: {preset.hue or '':3} "
f"Saturation: {preset.saturation or '':3} "
f"Brightness/Value: {preset.brightness or '':3} "
f"Temp: {preset.color_temp or '':4}"
)
return light_preset.preset_states_list
@presets.command(name="modify")
@click.argument("index", type=int)
@click.option("--brightness", type=int, required=False, default=None)
@click.option("--hue", type=int, required=False, default=None)
@click.option("--saturation", type=int, required=False, default=None)
@click.option("--temperature", type=int, required=False, default=None)
@pass_dev_or_child
async def presets_modify(dev: Device, index, brightness, hue, saturation, temperature):
"""Modify a preset."""
if not (light_preset := dev.modules.get(Module.LightPreset)):
error("Device does not support light presets")
return
max_index = len(light_preset.preset_states_list) - 1
if index > len(light_preset.preset_states_list) - 1:
error(f"Invalid index, must be between 0 and {max_index}")
return
if all([val is None for val in {brightness, hue, saturation, temperature}]):
error("Need to supply at least one option to modify.")
return
# Preset names have `Not set`` as the first value
preset_name = light_preset.preset_list[index + 1]
preset = light_preset.preset_states_list[index]
echo(f"Preset {preset_name} currently: {preset}")
if brightness is not None and preset.brightness is not None:
preset.brightness = brightness
if hue is not None and preset.hue is not None:
preset.hue = hue
if saturation is not None and preset.saturation is not None:
preset.saturation = saturation
if temperature is not None and preset.temperature is not None:
preset.color_temp = temperature
echo(f"Updating preset {preset_name} to: {preset}")
return await light_preset.save_preset(preset_name, preset)
@light.command()
@pass_dev_or_child
@click.option("--type", type=click.Choice(["soft", "hard"], case_sensitive=False))
@click.option("--last", is_flag=True)
@click.option("--preset", type=int)
async def turn_on_behavior(dev: Device, type, last, preset):
"""Modify bulb turn-on behavior."""
if dev.device_type is not Device.Type.Bulb or not isinstance(dev, IotBulb):
error("Presets only supported on iot bulbs")
return
settings = await dev.get_turn_on_behavior()
echo(f"Current turn on behavior: {settings}")
# Return if we are not setting the value
if not type and not last and not preset:
return settings
# If we are setting the value, the type has to be specified
if (last or preset) and type is None:
echo("To set the behavior, you need to define --type")
return
behavior = getattr(settings, type)
if last:
echo(f"Going to set {type} to last")
behavior.preset = None
elif preset is not None:
echo(f"Going to set {type} to preset {preset}")
behavior.preset = preset
return await dev.set_turn_on_behavior(settings)