mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-09 20:24:02 +00:00
Create common Time module and add time set cli command (#1157)
This commit is contained in:
125
kasa/cli/time.py
125
kasa/cli/time.py
@@ -5,15 +5,18 @@ from __future__ import annotations
|
||||
from datetime import datetime
|
||||
|
||||
import asyncclick as click
|
||||
import zoneinfo
|
||||
|
||||
from kasa import (
|
||||
Device,
|
||||
Module,
|
||||
)
|
||||
from kasa.smart import SmartDevice
|
||||
from kasa.iot import IotDevice
|
||||
from kasa.iot.iottimezone import get_matching_timezones
|
||||
|
||||
from .common import (
|
||||
echo,
|
||||
error,
|
||||
pass_dev,
|
||||
)
|
||||
|
||||
@@ -31,25 +34,127 @@ async def time(ctx: click.Context):
|
||||
async def time_get(dev: Device):
|
||||
"""Get the device time."""
|
||||
res = dev.time
|
||||
echo(f"Current time: {res}")
|
||||
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):
|
||||
async def time_sync(dev: Device, timezone: str | None, skip_confirm: bool):
|
||||
"""Set the device time to current time."""
|
||||
if not isinstance(dev, SmartDevice):
|
||||
raise NotImplementedError("setting time currently only implemented on smart")
|
||||
|
||||
if (time := dev.modules.get(Module.Time)) is None:
|
||||
echo("Device does not have time module")
|
||||
return
|
||||
|
||||
echo("Old time: %s" % time.time)
|
||||
now = datetime.now()
|
||||
|
||||
local_tz = datetime.now().astimezone().tzinfo
|
||||
await time.set_time(datetime.now(tz=local_tz))
|
||||
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("New time: %s" % time.time)
|
||||
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
|
||||
|
Reference in New Issue
Block a user