33from __future__ import annotations
44
55from dataclasses import dataclass
6- from typing import Any , Literal , Self , cast , overload
6+ from typing import Any , ClassVar , Self , cast
77
88from tuya_sharing import CustomerDevice
99
@@ -38,6 +38,7 @@ class TypeInformation[T]:
3838 As provided by the SDK, from `device.function` / `device.status_range`.
3939 """
4040
41+ _DPTYPE : ClassVar [DPType ]
4142 dpcode : str
4243 type_data : str | None = None
4344
@@ -52,19 +53,57 @@ def process_raw_value(
5253 return raw_value
5354
5455 @classmethod
55- def from_json (cls , dpcode : str , type_data : str ) -> Self | None :
56+ def _from_json (cls , dpcode : str , type_data : str ) -> Self | None :
5657 """Load JSON string and return a TypeInformation object."""
5758 return cls (dpcode = dpcode , type_data = type_data )
5859
60+ @classmethod
61+ def find_dpcode (
62+ cls ,
63+ device : CustomerDevice ,
64+ dpcodes : str | tuple [str , ...] | None ,
65+ * ,
66+ prefer_function : bool = False ,
67+ ) -> Self | None :
68+ """Find type information for a matching DP code available for this device."""
69+ if dpcodes is None :
70+ return None
71+
72+ if not isinstance (dpcodes , tuple ):
73+ dpcodes = (dpcodes ,)
74+
75+ lookup_tuple = (
76+ (device .function , device .status_range )
77+ if prefer_function
78+ else (device .status_range , device .function )
79+ )
80+
81+ for dpcode in dpcodes :
82+ for device_specs in lookup_tuple :
83+ if (
84+ (current_definition := device_specs .get (dpcode ))
85+ and parse_dptype (current_definition .type ) is cls ._DPTYPE
86+ and (
87+ type_information := cls ._from_json (
88+ dpcode = dpcode , type_data = current_definition .values
89+ )
90+ )
91+ ):
92+ return type_information
93+
94+ return None
95+
5996
6097@dataclass (kw_only = True )
6198class BitmapTypeInformation (TypeInformation [int ]):
6299 """Bitmap type information."""
63100
101+ _DPTYPE = DPType .BITMAP
102+
64103 label : list [str ]
65104
66105 @classmethod
67- def from_json (cls , dpcode : str , type_data : str ) -> Self | None :
106+ def _from_json (cls , dpcode : str , type_data : str ) -> Self | None :
68107 """Load JSON string and return a BitmapTypeInformation object."""
69108 if not (parsed := json_loads_object (type_data )):
70109 return None
@@ -79,6 +118,8 @@ def from_json(cls, dpcode: str, type_data: str) -> Self | None:
79118class BooleanTypeInformation (TypeInformation [bool ]):
80119 """Boolean type information."""
81120
121+ _DPTYPE = DPType .BOOLEAN
122+
82123 def process_raw_value (
83124 self , raw_value : Any | None , device : CustomerDevice
84125 ) -> bool | None :
@@ -107,6 +148,8 @@ def process_raw_value(
107148class EnumTypeInformation (TypeInformation [str ]):
108149 """Enum type information."""
109150
151+ _DPTYPE = DPType .ENUM
152+
110153 range : list [str ]
111154
112155 def process_raw_value (
@@ -133,7 +176,7 @@ def process_raw_value(
133176 return raw_value
134177
135178 @classmethod
136- def from_json (cls , dpcode : str , type_data : str ) -> Self | None :
179+ def _from_json (cls , dpcode : str , type_data : str ) -> Self | None :
137180 """Load JSON string and return an EnumTypeInformation object."""
138181 if not (parsed := json_loads_object (type_data )):
139182 return None
@@ -148,6 +191,8 @@ def from_json(cls, dpcode: str, type_data: str) -> Self | None:
148191class IntegerTypeInformation (TypeInformation [float ]):
149192 """Integer type information."""
150193
194+ _DPTYPE = DPType .INTEGER
195+
151196 min : int
152197 max : int
153198 scale : int
@@ -223,7 +268,7 @@ def process_raw_value(
223268 return raw_value / (10 ** self .scale )
224269
225270 @classmethod
226- def from_json (cls , dpcode : str , type_data : str ) -> Self | None :
271+ def _from_json (cls , dpcode : str , type_data : str ) -> Self | None :
227272 """Load JSON string and return an IntegerTypeInformation object."""
228273 if not (parsed := cast (dict [str , Any ] | None , json_loads_object (type_data ))):
229274 return None
@@ -239,101 +284,22 @@ def from_json(cls, dpcode: str, type_data: str) -> Self | None:
239284 )
240285
241286
242- _TYPE_INFORMATION_MAPPINGS : dict [DPType , type [TypeInformation ]] = {
243- DPType .BITMAP : BitmapTypeInformation ,
244- DPType .BOOLEAN : BooleanTypeInformation ,
245- DPType .ENUM : EnumTypeInformation ,
246- DPType .INTEGER : IntegerTypeInformation ,
247- DPType .JSON : TypeInformation ,
248- DPType .RAW : TypeInformation ,
249- DPType .STRING : TypeInformation ,
250- }
251-
252-
253- @overload
254- def find_dpcode (
255- device : CustomerDevice ,
256- dpcodes : str | tuple [str , ...] | None ,
257- * ,
258- prefer_function : bool = False ,
259- dptype : Literal [DPType .BITMAP ],
260- ) -> BitmapTypeInformation | None : ...
261-
262-
263- @overload
264- def find_dpcode (
265- device : CustomerDevice ,
266- dpcodes : str | tuple [str , ...] | None ,
267- * ,
268- prefer_function : bool = False ,
269- dptype : Literal [DPType .BOOLEAN ],
270- ) -> BooleanTypeInformation | None : ...
271-
272-
273- @overload
274- def find_dpcode (
275- device : CustomerDevice ,
276- dpcodes : str | tuple [str , ...] | None ,
277- * ,
278- prefer_function : bool = False ,
279- dptype : Literal [DPType .ENUM ],
280- ) -> EnumTypeInformation | None : ...
281-
282-
283- @overload
284- def find_dpcode (
285- device : CustomerDevice ,
286- dpcodes : str | tuple [str , ...] | None ,
287- * ,
288- prefer_function : bool = False ,
289- dptype : Literal [DPType .INTEGER ],
290- ) -> IntegerTypeInformation | None : ...
291-
292-
293- @overload
294- def find_dpcode (
295- device : CustomerDevice ,
296- dpcodes : str | tuple [str , ...] | None ,
297- * ,
298- prefer_function : bool = False ,
299- dptype : Literal [DPType .JSON , DPType .RAW ],
300- ) -> TypeInformation | None : ...
301-
302-
303- def find_dpcode (
304- device : CustomerDevice ,
305- dpcodes : str | tuple [str , ...] | None ,
306- * ,
307- prefer_function : bool = False ,
308- dptype : DPType ,
309- ) -> TypeInformation | None :
310- """Find type information for a matching DP code available for this device."""
311- if not (type_information_cls := _TYPE_INFORMATION_MAPPINGS .get (dptype )):
312- raise NotImplementedError (f"find_dpcode not supported for { dptype } " )
313-
314- if dpcodes is None :
315- return None
287+ @dataclass (kw_only = True )
288+ class JsonTypeInformation (TypeInformation [Any ]):
289+ """Json type information."""
316290
317- if not isinstance (dpcodes , tuple ):
318- dpcodes = (dpcodes ,)
319-
320- lookup_tuple = (
321- (device .function , device .status_range )
322- if prefer_function
323- else (device .status_range , device .function )
324- )
325-
326- for dpcode in dpcodes :
327- for device_specs in lookup_tuple :
328- if (
329- (current_definition := device_specs .get (dpcode ))
330- and parse_dptype (current_definition .type ) is dptype
331- and (
332- type_information := type_information_cls .from_json (
333- dpcode = dpcode , type_data = current_definition .values
334- )
335- )
336- ):
337- return type_information
291+ _DPTYPE = DPType .JSON
292+
293+
294+ @dataclass (kw_only = True )
295+ class RawTypeInformation (TypeInformation [Any ]):
296+ """Raw type information."""
297+
298+ _DPTYPE = DPType .RAW
299+
300+
301+ @dataclass (kw_only = True )
302+ class StringTypeInformation (TypeInformation [str ]):
303+ """String type information."""
338304
339- return None
305+ _DPTYPE = DPType . STRING
0 commit comments