141 lines
3.9 KiB
Python
141 lines
3.9 KiB
Python
"""Tuya Home Assistant Base Device Model."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
from dataclasses import dataclass
|
|
import json
|
|
import struct
|
|
from typing import Self
|
|
|
|
from .const import DPCode
|
|
from .util import remap_value
|
|
|
|
|
|
@dataclass
|
|
class IntegerTypeData:
|
|
"""Integer Type Data."""
|
|
|
|
dpcode: DPCode
|
|
min: int
|
|
max: int
|
|
scale: float
|
|
step: float
|
|
unit: str | None = None
|
|
type: str | None = None
|
|
|
|
@property
|
|
def max_scaled(self) -> float:
|
|
"""Return the max scaled."""
|
|
return self.scale_value(self.max)
|
|
|
|
@property
|
|
def min_scaled(self) -> float:
|
|
"""Return the min scaled."""
|
|
return self.scale_value(self.min)
|
|
|
|
@property
|
|
def step_scaled(self) -> float:
|
|
"""Return the step scaled."""
|
|
return self.step / (10**self.scale)
|
|
|
|
def scale_value(self, value: float) -> float:
|
|
"""Scale a value."""
|
|
return value / (10**self.scale)
|
|
|
|
def scale_value_back(self, value: float) -> int:
|
|
"""Return raw value for scaled."""
|
|
return int(value * (10**self.scale))
|
|
|
|
def remap_value_to(
|
|
self,
|
|
value: float,
|
|
to_min: float = 0,
|
|
to_max: float = 255,
|
|
reverse: bool = False,
|
|
) -> float:
|
|
"""Remap a value from this range to a new range."""
|
|
return remap_value(value, self.min, self.max, to_min, to_max, reverse)
|
|
|
|
def remap_value_from(
|
|
self,
|
|
value: float,
|
|
from_min: float = 0,
|
|
from_max: float = 255,
|
|
reverse: bool = False,
|
|
) -> float:
|
|
"""Remap a value from its current range to this range."""
|
|
return remap_value(value, from_min, from_max, self.min, self.max, reverse)
|
|
|
|
@classmethod
|
|
def from_json(cls, dpcode: DPCode, data: str) -> IntegerTypeData | None:
|
|
"""Load JSON string and return a IntegerTypeData object."""
|
|
if not (parsed := json.loads(data)):
|
|
return None
|
|
|
|
return cls(
|
|
dpcode,
|
|
min=int(parsed["min"]),
|
|
max=int(parsed["max"]),
|
|
scale=float(parsed["scale"]),
|
|
step=max(float(parsed["step"]), 1),
|
|
unit=parsed.get("unit"),
|
|
type=parsed.get("type"),
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class EnumTypeData:
|
|
"""Enum Type Data."""
|
|
|
|
dpcode: DPCode
|
|
range: list[str]
|
|
|
|
@classmethod
|
|
def from_json(cls, dpcode: DPCode, data: str) -> EnumTypeData | None:
|
|
"""Load JSON string and return a EnumTypeData object."""
|
|
if not (parsed := json.loads(data)):
|
|
return None
|
|
return cls(dpcode, **parsed)
|
|
|
|
|
|
class ComplexValue:
|
|
"""Complex value (for JSON/RAW parsing)."""
|
|
|
|
@classmethod
|
|
def from_json(cls, data: str) -> Self:
|
|
"""Load JSON string and return a ComplexValue object."""
|
|
raise NotImplementedError("from_json is not implemented for this type")
|
|
|
|
@classmethod
|
|
def from_raw(cls, data: str) -> Self | None:
|
|
"""Decode base64 string and return a ComplexValue object."""
|
|
raise NotImplementedError("from_raw is not implemented for this type")
|
|
|
|
|
|
@dataclass
|
|
class ElectricityValue(ComplexValue):
|
|
"""Electricity complex value."""
|
|
|
|
electriccurrent: str | None = None
|
|
power: str | None = None
|
|
voltage: str | None = None
|
|
|
|
@classmethod
|
|
def from_json(cls, data: str) -> Self:
|
|
"""Load JSON string and return a ElectricityValue object."""
|
|
return cls(**json.loads(data.lower()))
|
|
|
|
@classmethod
|
|
def from_raw(cls, data: str) -> Self | None:
|
|
"""Decode base64 string and return a ElectricityValue object."""
|
|
raw = base64.b64decode(data)
|
|
if len(raw) == 0:
|
|
return None
|
|
voltage = struct.unpack(">H", raw[0:2])[0] / 10.0
|
|
electriccurrent = struct.unpack(">L", b"\x00" + raw[2:5])[0] / 1000.0
|
|
power = struct.unpack(">L", b"\x00" + raw[5:8])[0] / 1000.0
|
|
return cls(
|
|
electriccurrent=str(electriccurrent), power=str(power), voltage=str(voltage)
|
|
)
|