66import base64
77from dataclasses import dataclass
88import json
9- import struct
109from typing import Any , Literal , Self , overload
1110
1211from tuya_sharing import CustomerDevice
1312
13+ from homeassistant .util .json import json_loads
14+
1415from .const import DPCode , DPType
15- from .util import remap_value
16+ from .util import parse_dptype , remap_value
1617
1718
1819@dataclass
@@ -134,6 +135,8 @@ def from_json(cls, dpcode: DPCode, data: str) -> Self | None:
134135 DPType .BOOLEAN : TypeInformation ,
135136 DPType .ENUM : EnumTypeData ,
136137 DPType .INTEGER : IntegerTypeData ,
138+ DPType .JSON : TypeInformation ,
139+ DPType .RAW : TypeInformation ,
137140}
138141
139142
@@ -144,6 +147,9 @@ class DPCodeWrapper(ABC):
144147 access read conversion routines.
145148 """
146149
150+ native_unit : str | None = None
151+ suggested_unit : str | None = None
152+
147153 def __init__ (self , dpcode : str ) -> None :
148154 """Init DPCodeWrapper."""
149155 self .dpcode = dpcode
@@ -210,6 +216,20 @@ def find_dpcode(
210216 return None
211217
212218
219+ class DPCodeBase64Wrapper (DPCodeTypeInformationWrapper [TypeInformation ]):
220+ """Wrapper to extract information from a RAW/binary value."""
221+
222+ DPTYPE = DPType .RAW
223+
224+ def read_bytes (self , device : CustomerDevice ) -> bytes | None :
225+ """Read the device value for the dpcode."""
226+ if (raw_value := self ._read_device_status_raw (device )) is None or (
227+ len (decoded := base64 .b64decode (raw_value )) == 0
228+ ):
229+ return None
230+ return decoded
231+
232+
213233class DPCodeBooleanWrapper (DPCodeTypeInformationWrapper [TypeInformation ]):
214234 """Simple wrapper for boolean values.
215235
@@ -235,6 +255,18 @@ def _convert_value_to_raw_value(
235255 raise ValueError (f"Invalid boolean value `{ value } `" )
236256
237257
258+ class DPCodeJsonWrapper (DPCodeTypeInformationWrapper [TypeInformation ]):
259+ """Wrapper to extract information from a JSON value."""
260+
261+ DPTYPE = DPType .JSON
262+
263+ def read_json (self , device : CustomerDevice ) -> Any | None :
264+ """Read the device value for the dpcode."""
265+ if (raw_value := self ._read_device_status_raw (device )) is None :
266+ return None
267+ return json_loads (raw_value )
268+
269+
238270class DPCodeEnumWrapper (DPCodeTypeInformationWrapper [EnumTypeData ]):
239271 """Simple wrapper for EnumTypeData values."""
240272
@@ -268,6 +300,11 @@ class DPCodeIntegerWrapper(DPCodeTypeInformationWrapper[IntegerTypeData]):
268300
269301 DPTYPE = DPType .INTEGER
270302
303+ def __init__ (self , dpcode : str , type_information : IntegerTypeData ) -> None :
304+ """Init DPCodeIntegerWrapper."""
305+ super ().__init__ (dpcode , type_information )
306+ self .native_unit = type_information .unit
307+
271308 def read_device_status (self , device : CustomerDevice ) -> float | None :
272309 """Read the device value for the dpcode.
273310
@@ -352,6 +389,16 @@ def find_dpcode(
352389) -> IntegerTypeData | None : ...
353390
354391
392+ @overload
393+ def find_dpcode (
394+ device : CustomerDevice ,
395+ dpcodes : str | DPCode | tuple [DPCode , ...] | None ,
396+ * ,
397+ prefer_function : bool = False ,
398+ dptype : Literal [DPType .BOOLEAN , DPType .JSON , DPType .RAW ],
399+ ) -> TypeInformation | None : ...
400+
401+
355402def find_dpcode (
356403 device : CustomerDevice ,
357404 dpcodes : str | DPCode | tuple [DPCode , ...] | None ,
@@ -381,7 +428,7 @@ def find_dpcode(
381428 for device_specs in lookup_tuple :
382429 if (
383430 (current_definition := device_specs .get (dpcode ))
384- and current_definition .type == dptype
431+ and parse_dptype ( current_definition .type ) is dptype
385432 and (
386433 type_information := type_information_cls .from_json (
387434 dpcode , current_definition .values
@@ -391,44 +438,3 @@ def find_dpcode(
391438 return type_information
392439
393440 return None
394-
395-
396- class ComplexValue :
397- """Complex value (for JSON/RAW parsing)."""
398-
399- @classmethod
400- def from_json (cls , data : str ) -> Self :
401- """Load JSON string and return a ComplexValue object."""
402- raise NotImplementedError ("from_json is not implemented for this type" )
403-
404- @classmethod
405- def from_raw (cls , data : str ) -> Self | None :
406- """Decode base64 string and return a ComplexValue object."""
407- raise NotImplementedError ("from_raw is not implemented for this type" )
408-
409-
410- @dataclass
411- class ElectricityValue (ComplexValue ):
412- """Electricity complex value."""
413-
414- electriccurrent : str | None = None
415- power : str | None = None
416- voltage : str | None = None
417-
418- @classmethod
419- def from_json (cls , data : str ) -> Self :
420- """Load JSON string and return a ElectricityValue object."""
421- return cls (** json .loads (data .lower ()))
422-
423- @classmethod
424- def from_raw (cls , data : str ) -> Self | None :
425- """Decode base64 string and return a ElectricityValue object."""
426- raw = base64 .b64decode (data )
427- if len (raw ) == 0 :
428- return None
429- voltage = struct .unpack (">H" , raw [0 :2 ])[0 ] / 10.0
430- electriccurrent = struct .unpack (">L" , b"\x00 " + raw [2 :5 ])[0 ] / 1000.0
431- power = struct .unpack (">L" , b"\x00 " + raw [5 :8 ])[0 ] / 1000.0
432- return cls (
433- electriccurrent = str (electriccurrent ), power = str (power ), voltage = str (voltage )
434- )
0 commit comments