90
90
import socket
91
91
import struct
92
92
from asyncio .transports import DatagramTransport
93
+ from dataclasses import dataclass , field
93
94
from pprint import pformat as pf
94
95
from typing import (
95
96
TYPE_CHECKING ,
108
109
# When support for cpython older than 3.11 is dropped
109
110
# async_timeout can be replaced with asyncio.timeout
110
111
from async_timeout import timeout as asyncio_timeout
111
- from pydantic .v1 import BaseModel , ValidationError
112
+ from mashumaro import field_options
113
+ from mashumaro .config import BaseConfig
112
114
113
115
from kasa import Device
114
116
from kasa .credentials import Credentials
130
132
from kasa .experimental import Experimental
131
133
from kasa .iot .iotdevice import IotDevice
132
134
from kasa .iotprotocol import REDACTORS as IOT_REDACTORS
135
+ from kasa .json import DataClassJSONMixin
133
136
from kasa .json import dumps as json_dumps
134
137
from kasa .json import loads as json_loads
135
138
from kasa .protocol import mask_mac , redact_data
@@ -647,7 +650,7 @@ async def try_connect_all(
647
650
def _get_device_class (info : dict ) -> type [Device ]:
648
651
"""Find SmartDevice subclass for device described by passed data."""
649
652
if "result" in info :
650
- discovery_result = DiscoveryResult ( ** info ["result" ])
653
+ discovery_result = DiscoveryResult . from_dict ( info ["result" ])
651
654
https = discovery_result .mgt_encrypt_schm .is_support_https
652
655
dev_class = get_device_class_from_family (
653
656
discovery_result .device_type , https = https
@@ -721,12 +724,8 @@ def _get_device_instance(
721
724
f"Unable to read response from device: { config .host } : { ex } "
722
725
) from ex
723
726
try :
724
- discovery_result = DiscoveryResult (** info ["result" ])
725
- if (
726
- encrypt_info := discovery_result .encrypt_info
727
- ) and encrypt_info .sym_schm == "AES" :
728
- Discover ._decrypt_discovery_data (discovery_result )
729
- except ValidationError as ex :
727
+ discovery_result = DiscoveryResult .from_dict (info ["result" ])
728
+ except Exception as ex :
730
729
if debug_enabled :
731
730
data = (
732
731
redact_data (info , NEW_DISCOVERY_REDACTORS )
@@ -742,6 +741,16 @@ def _get_device_instance(
742
741
f"Unable to parse discovery from device: { config .host } : { ex } " ,
743
742
host = config .host ,
744
743
) from ex
744
+ # Decrypt the data
745
+ if (
746
+ encrypt_info := discovery_result .encrypt_info
747
+ ) and encrypt_info .sym_schm == "AES" :
748
+ try :
749
+ Discover ._decrypt_discovery_data (discovery_result )
750
+ except Exception :
751
+ _LOGGER .exception (
752
+ "Unable to decrypt discovery data %s: %s" , config .host , data
753
+ )
745
754
746
755
type_ = discovery_result .device_type
747
756
encrypt_schm = discovery_result .mgt_encrypt_schm
@@ -754,7 +763,7 @@ def _get_device_instance(
754
763
raise UnsupportedDeviceError (
755
764
f"Unsupported device { config .host } of type { type_ } "
756
765
+ "with no encryption type" ,
757
- discovery_result = discovery_result .get_dict (),
766
+ discovery_result = discovery_result .to_dict (),
758
767
host = config .host ,
759
768
)
760
769
config .connection_type = DeviceConnectionParameters .from_values (
@@ -767,7 +776,7 @@ def _get_device_instance(
767
776
raise UnsupportedDeviceError (
768
777
f"Unsupported device { config .host } of type { type_ } "
769
778
+ f"with encrypt_type { discovery_result .mgt_encrypt_schm .encrypt_type } " ,
770
- discovery_result = discovery_result .get_dict (),
779
+ discovery_result = discovery_result .to_dict (),
771
780
host = config .host ,
772
781
) from ex
773
782
if (
@@ -778,7 +787,7 @@ def _get_device_instance(
778
787
_LOGGER .warning ("Got unsupported device type: %s" , type_ )
779
788
raise UnsupportedDeviceError (
780
789
f"Unsupported device { config .host } of type { type_ } : { info } " ,
781
- discovery_result = discovery_result .get_dict (),
790
+ discovery_result = discovery_result .to_dict (),
782
791
host = config .host ,
783
792
)
784
793
if (protocol := get_protocol (config )) is None :
@@ -788,7 +797,7 @@ def _get_device_instance(
788
797
raise UnsupportedDeviceError (
789
798
f"Unsupported encryption scheme { config .host } of "
790
799
+ f"type { config .connection_type .to_dict ()} : { info } " ,
791
- discovery_result = discovery_result .get_dict (),
800
+ discovery_result = discovery_result .to_dict (),
792
801
host = config .host ,
793
802
)
794
803
@@ -801,42 +810,59 @@ def _get_device_instance(
801
810
_LOGGER .debug ("[DISCOVERY] %s << %s" , config .host , pf (data ))
802
811
device = device_class (config .host , protocol = protocol )
803
812
804
- di = discovery_result .get_dict ()
813
+ di = discovery_result .to_dict ()
805
814
di ["model" ], _ , _ = discovery_result .device_model .partition ("(" )
806
815
device .update_from_discover_info (di )
807
816
return device
808
817
809
818
810
- class EncryptionScheme (BaseModel ):
819
+ class _DiscoveryBaseMixin (DataClassJSONMixin ):
820
+ """Base class for serialization mixin."""
821
+
822
+ class Config (BaseConfig ):
823
+ """Serialization config."""
824
+
825
+ omit_none = True
826
+ omit_default = True
827
+ serialize_by_alias = True
828
+
829
+
830
+ @dataclass
831
+ class EncryptionScheme (_DiscoveryBaseMixin ):
811
832
"""Base model for encryption scheme of discovery result."""
812
833
813
834
is_support_https : bool
814
- encrypt_type : Optional [str ] # noqa: UP007
835
+ encrypt_type : Optional [str ] = None # noqa: UP007
815
836
http_port : Optional [int ] = None # noqa: UP007
816
837
lv : Optional [int ] = None # noqa: UP007
817
838
818
839
819
- class EncryptionInfo (BaseModel ):
840
+ @dataclass
841
+ class EncryptionInfo (_DiscoveryBaseMixin ):
820
842
"""Base model for encryption info of discovery result."""
821
843
822
844
sym_schm : str
823
845
key : str
824
846
data : str
825
847
826
848
827
- class DiscoveryResult (BaseModel ):
849
+ @dataclass
850
+ class DiscoveryResult (_DiscoveryBaseMixin ):
828
851
"""Base model for discovery result."""
829
852
830
853
device_type : str
831
854
device_model : str
832
- device_name : Optional [ str ] # noqa: UP007
855
+ device_id : str
833
856
ip : str
834
857
mac : str
835
858
mgt_encrypt_schm : EncryptionScheme
859
+ device_name : Optional [str ] = None # noqa: UP007
836
860
encrypt_info : Optional [EncryptionInfo ] = None # noqa: UP007
837
861
encrypt_type : Optional [list [str ]] = None # noqa: UP007
838
862
decrypted_data : Optional [dict ] = None # noqa: UP007
839
- device_id : str
863
+ is_reset_wifi : Optional [bool ] = field ( # noqa: UP007
864
+ metadata = field_options (alias = "isResetWiFi" ), default = None
865
+ )
840
866
841
867
firmware_version : Optional [str ] = None # noqa: UP007
842
868
hardware_version : Optional [str ] = None # noqa: UP007
@@ -845,12 +871,3 @@ class DiscoveryResult(BaseModel):
845
871
is_support_iot_cloud : Optional [bool ] = None # noqa: UP007
846
872
obd_src : Optional [str ] = None # noqa: UP007
847
873
factory_default : Optional [bool ] = None # noqa: UP007
848
-
849
- def get_dict (self ) -> dict :
850
- """Return a dict for this discovery result.
851
-
852
- containing only the values actually set and with aliases as field names.
853
- """
854
- return self .dict (
855
- by_alias = False , exclude_unset = True , exclude_none = True , exclude_defaults = True
856
- )
0 commit comments