mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 11:13:34 +00:00
a01247d48f
Python 3.11 ships with latest Debian Bookworm. pypy is not that widely used with this library based on statistics. It could be added back when pypy supports python 3.11.
161 lines
4.2 KiB
Python
161 lines
4.2 KiB
Python
"""Module for cli time commands.."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import zoneinfo
|
|
from datetime import datetime
|
|
|
|
import asyncclick as click
|
|
|
|
from kasa import (
|
|
Device,
|
|
Module,
|
|
)
|
|
from kasa.iot import IotDevice
|
|
from kasa.iot.iottimezone import get_matching_timezones
|
|
|
|
from .common import (
|
|
echo,
|
|
error,
|
|
pass_dev,
|
|
)
|
|
|
|
|
|
@click.group(invoke_without_command=True)
|
|
@click.pass_context
|
|
async def time(ctx: click.Context) -> None:
|
|
"""Get and set time."""
|
|
if ctx.invoked_subcommand is None:
|
|
await ctx.invoke(time_get)
|
|
|
|
|
|
@time.command(name="get")
|
|
@pass_dev
|
|
async def time_get(dev: Device):
|
|
"""Get the device time."""
|
|
res = dev.time
|
|
echo(f"Current time: {dev.time} ({dev.timezone})")
|
|
return res
|
|
|
|
|
|
@time.command(name="sync")
|
|
@click.option(
|
|
"--timezone",
|
|
type=str,
|
|
required=False,
|
|
default=None,
|
|
help="IANA timezone name, will use current device timezone if not provided.",
|
|
)
|
|
@click.option(
|
|
"--skip-confirm",
|
|
type=str,
|
|
required=False,
|
|
default=False,
|
|
is_flag=True,
|
|
help="Do not ask to confirm the timezone if an exact match is not found.",
|
|
)
|
|
@pass_dev
|
|
async def time_sync(dev: Device, timezone: str | None, skip_confirm: bool):
|
|
"""Set the device time to current time."""
|
|
if (time := dev.modules.get(Module.Time)) is None:
|
|
echo("Device does not have time module")
|
|
return
|
|
|
|
now = datetime.now()
|
|
|
|
tzinfo: zoneinfo.ZoneInfo | None = None
|
|
if timezone:
|
|
tzinfo = await _get_timezone(dev, timezone, skip_confirm)
|
|
if tzinfo.utcoffset(now) != now.astimezone().utcoffset():
|
|
error(
|
|
f"{timezone} has a different utc offset to local time,"
|
|
+ "syncing will produce unexpected results."
|
|
)
|
|
now = now.replace(tzinfo=tzinfo)
|
|
|
|
echo(f"Old time: {time.time} ({time.timezone})")
|
|
|
|
await time.set_time(now)
|
|
|
|
await dev.update()
|
|
echo(f"New time: {time.time} ({time.timezone})")
|
|
|
|
|
|
@time.command(name="set")
|
|
@click.argument("year", type=int)
|
|
@click.argument("month", type=int)
|
|
@click.argument("day", type=int)
|
|
@click.argument("hour", type=int)
|
|
@click.argument("minute", type=int)
|
|
@click.argument("seconds", type=int, required=False, default=0)
|
|
@click.option(
|
|
"--timezone",
|
|
type=str,
|
|
required=False,
|
|
default=None,
|
|
help="IANA timezone name, will use current device timezone if not provided.",
|
|
)
|
|
@click.option(
|
|
"--skip-confirm",
|
|
type=bool,
|
|
required=False,
|
|
default=False,
|
|
is_flag=True,
|
|
help="Do not ask to confirm the timezone if an exact match is not found.",
|
|
)
|
|
@pass_dev
|
|
async def time_set(
|
|
dev: Device,
|
|
year: int,
|
|
month: int,
|
|
day: int,
|
|
hour: int,
|
|
minute: int,
|
|
seconds: int,
|
|
timezone: str | None,
|
|
skip_confirm: bool,
|
|
):
|
|
"""Set the device time to the provided time."""
|
|
if (time := dev.modules.get(Module.Time)) is None:
|
|
echo("Device does not have time module")
|
|
return
|
|
|
|
tzinfo: zoneinfo.ZoneInfo | None = None
|
|
if timezone:
|
|
tzinfo = await _get_timezone(dev, timezone, skip_confirm)
|
|
|
|
echo(f"Old time: {time.time} ({time.timezone})")
|
|
|
|
await time.set_time(datetime(year, month, day, hour, minute, seconds, 0, tzinfo))
|
|
|
|
await dev.update()
|
|
echo(f"New time: {time.time} ({time.timezone})")
|
|
|
|
|
|
async def _get_timezone(dev, timezone, skip_confirm) -> zoneinfo.ZoneInfo:
|
|
"""Get the tzinfo from the timezone or return none."""
|
|
tzinfo: zoneinfo.ZoneInfo | None = None
|
|
|
|
if timezone not in zoneinfo.available_timezones():
|
|
error(f"{timezone} is not a valid IANA timezone.")
|
|
|
|
tzinfo = zoneinfo.ZoneInfo(timezone)
|
|
if skip_confirm is False and isinstance(dev, IotDevice):
|
|
matches = await get_matching_timezones(tzinfo)
|
|
if not matches:
|
|
error(f"Device cannot support {timezone} timezone.")
|
|
first = matches[0]
|
|
msg = (
|
|
f"An exact match for {timezone} could not be found, "
|
|
+ f"timezone will be set to {first}"
|
|
)
|
|
if len(matches) == 1:
|
|
click.confirm(msg, abort=True)
|
|
else:
|
|
msg = (
|
|
f"Supported timezones matching {timezone} are {', '.join(matches)}\n"
|
|
+ msg
|
|
)
|
|
click.confirm(msg, abort=True)
|
|
return tzinfo
|