mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-26 13:03:34 +00:00
159 lines
4.8 KiB
Python
159 lines
4.8 KiB
Python
"""Cloud module implementation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from pydantic.v1 import BaseModel
|
|
|
|
from datetime import date
|
|
from typing import Optional
|
|
|
|
from ...feature import Feature
|
|
from ...firmware import (
|
|
Firmware,
|
|
UpdateResult,
|
|
)
|
|
from ...firmware import (
|
|
FirmwareUpdate as FirmwareUpdateInterface,
|
|
)
|
|
from ..iotmodule import IotModule, merge
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
class CloudInfo(BaseModel):
|
|
"""Container for cloud settings."""
|
|
|
|
binded: bool
|
|
cld_connection: int
|
|
fwDlPage: str
|
|
fwNotifyType: int
|
|
illegalType: int
|
|
server: str
|
|
stopConnect: int
|
|
tcspInfo: str
|
|
tcspStatus: int
|
|
username: str
|
|
|
|
|
|
class FirmwareUpdate(BaseModel):
|
|
"""Update info status object."""
|
|
|
|
status: int = Field(alias="fwType")
|
|
version: Optional[str] = Field(alias="fwVer", default=None) # noqa: UP007
|
|
release_date: Optional[date] = Field(alias="fwReleaseDate", default=None) # noqa: UP007
|
|
release_notes: Optional[str] = Field(alias="fwReleaseLog", default=None) # noqa: UP007
|
|
url: Optional[str] = Field(alias="fwUrl", default=None) # noqa: UP007
|
|
|
|
@validator("release_date", pre=True)
|
|
def _release_date_optional(cls, v):
|
|
if not v:
|
|
return None
|
|
|
|
return v
|
|
|
|
@property
|
|
def update_available(self):
|
|
"""Return True if update available."""
|
|
if self.status != 0:
|
|
return True
|
|
return False
|
|
|
|
|
|
class Cloud(IotModule, Firmware):
|
|
"""Module implementing support for cloud services."""
|
|
|
|
def __init__(self, device, module):
|
|
super().__init__(device, module)
|
|
self._add_feature(
|
|
Feature(
|
|
device=device,
|
|
container=self,
|
|
id="cloud_connection",
|
|
name="Cloud connection",
|
|
icon="mdi:cloud",
|
|
attribute_getter="is_connected",
|
|
type=Feature.Type.BinarySensor,
|
|
category=Feature.Category.Info,
|
|
)
|
|
)
|
|
|
|
@property
|
|
def is_connected(self) -> bool:
|
|
"""Return true if device is connected to the cloud."""
|
|
return self.info.binded
|
|
|
|
def query(self):
|
|
"""Request cloud connectivity info."""
|
|
req = self.query_for_command("get_info")
|
|
|
|
# TODO: this is problematic, as it will fail the whole query on some
|
|
# devices if they are not connected to the internet
|
|
if self._module in self._device._last_update and self.is_connected:
|
|
req = merge(req, self.get_available_firmwares())
|
|
|
|
return req
|
|
|
|
@property
|
|
def info(self) -> CloudInfo:
|
|
"""Return information about the cloud connectivity."""
|
|
return CloudInfo.parse_obj(self.data["get_info"])
|
|
|
|
async def get_available_firmwares(self):
|
|
"""Return list of available firmwares."""
|
|
return await self.call("get_intl_fw_list")
|
|
|
|
async def get_firmware_update(self) -> FirmwareUpdate:
|
|
"""Return firmware update information."""
|
|
try:
|
|
available_fws = (await self.get_available_firmwares()).get("fw_list", [])
|
|
if not available_fws:
|
|
return FirmwareUpdate(fwType=0)
|
|
if len(available_fws) > 1:
|
|
_LOGGER.warning(
|
|
"Got more than one update, using the first one: %s", available_fws
|
|
)
|
|
return FirmwareUpdate.parse_obj(next(iter(available_fws)))
|
|
except Exception as ex:
|
|
_LOGGER.warning("Unable to check for firmware update: %s", ex)
|
|
return FirmwareUpdate(fwType=0)
|
|
|
|
async def set_server(self, url: str):
|
|
"""Set the update server URL."""
|
|
return await self.call("set_server_url", {"server": url})
|
|
|
|
async def connect(self, username: str, password: str):
|
|
"""Login to the cloud using given information."""
|
|
return await self.call("bind", {"username": username, "password": password})
|
|
|
|
async def disconnect(self):
|
|
"""Disconnect from the cloud."""
|
|
return await self.call("unbind")
|
|
|
|
async def update_firmware(self, *, progress_cb=None) -> UpdateResult:
|
|
"""Perform firmware update."""
|
|
raise NotImplementedError
|
|
i = 0
|
|
import asyncio
|
|
|
|
while i < 100:
|
|
await asyncio.sleep(1)
|
|
if progress_cb is not None:
|
|
await progress_cb(i)
|
|
i += 10
|
|
|
|
return UpdateResult("")
|
|
|
|
async def check_for_updates(self) -> FirmwareUpdateInterface:
|
|
"""Return firmware update information."""
|
|
fw = await self.get_firmware_update()
|
|
|
|
return FirmwareUpdateInterface(
|
|
update_available=fw.update_available,
|
|
current_version=self._device.hw_info.get("sw_ver"),
|
|
available_version=fw.version,
|
|
release_date=fw.release_date,
|
|
release_notes=fw.release_notes,
|
|
)
|