22import ast
33import asyncio
44import logging
5- from typing import NoReturn
5+ from typing import NoReturn , Union
66
77from msmart import __version__
8+ from msmart .base_device import Device
89from msmart .cloud import CloudError , NetHomePlusCloud , SmartHomeCloud
9- from msmart .const import DEFAULT_CLOUD_REGION
10+ from msmart .const import DEFAULT_CLOUD_REGION , DeviceType
1011from msmart .device import AirConditioner as AC
12+ from msmart .device import CommercialAirConditioner as CC
1113from msmart .discover import Discover
1214from msmart .lan import AuthenticationError
1315from msmart .utils import MideaIntEnum
1921
2022DEFAULT_CLOUD_ACCOUNT , DEFAULT_CLOUD_PASSWORD = CLOUD_CREDENTIALS [DEFAULT_CLOUD_REGION ]
2123
24+ DEVICE_TYPES = {
25+ "AC" : DeviceType .AIR_CONDITIONER ,
26+ "CC" : DeviceType .COMMERCIAL_AC ,
27+ }
28+
2229
2330async def _discover (args ) -> None :
2431 """Discover Midea devices and print configuration information."""
@@ -43,16 +50,18 @@ async def _discover(args) -> None:
4350
4451 if isinstance (device , AC ):
4552 device = super (AC , device )
53+ elif isinstance (device , CC ):
54+ device = super (CC , device )
4655
4756 _LOGGER .info ("Found device:\n %s" , device .to_dict ())
4857
4958
50- async def _connect (args ) -> AC :
59+ async def _connect (args ) -> Union [ AC , CC ] :
5160 """Connect to a device directly or via discovery."""
5261
53- if args .auto and (args .token or args .key or args .device_id ):
62+ if args .auto and (args .token or args .key or args .device_id or args . device_type ):
5463 _LOGGER .warning (
55- "--token, --key and --id are ignored with --auto option." )
64+ "--token, --key, --id and --device_type are ignored with --auto option." )
5665
5766 if args .auto :
5867 # Use discovery to automatically connect and authenticate with device
@@ -63,16 +72,22 @@ async def _connect(args) -> AC:
6372 _LOGGER .error ("Device not found." )
6473 exit (1 )
6574 else :
66- # Manually create device and authenticate
67- device = AC (ip = args .host , port = 6444 , device_id = args .device_id )
75+ device = Device .construct (
76+ type = DEVICE_TYPES .get (
77+ args .device_type , DeviceType .AIR_CONDITIONER ),
78+ ip = args .host ,
79+ port = 6444 ,
80+ device_id = args .device_id
81+ )
82+
6883 if args .token and args .key :
6984 try :
7085 await device .authenticate (args .token , args .key )
7186 except AuthenticationError as e :
7287 _LOGGER .error ("Authentication failed. Error: %s" , e )
7388 exit (1 )
7489
75- if not isinstance (device , AC ):
90+ if not isinstance (device , ( AC , CC ) ):
7691 _LOGGER .error ("Device is not supported." )
7792 exit (1 )
7893
@@ -93,24 +108,15 @@ async def _query(args) -> None:
93108 _LOGGER .error ("Device is not online." )
94109 exit (1 )
95110
96- # TODO method to get caps in string format
97- _LOGGER .info ("%s" , str ({
98- "supported_modes" : device .supported_operation_modes ,
99- "supported_swing_modes" : device .supported_swing_modes ,
100- "supported_fan_speeds" : device .supported_fan_speeds ,
101- "supports_custom_fan_speed" : device .supports_custom_fan_speed ,
102- "supports_eco" : device .supports_eco ,
103- "supports_turbo" : device .supports_turbo ,
104- "supports_freeze_protection" : device .supports_freeze_protection ,
105- "supports_display_control" : device .supports_display_control ,
106- "supports_filter_reminder" : device .supports_filter_reminder ,
107- "max_target_temperature" : device .max_target_temperature ,
108- "min_target_temperature" : device .min_target_temperature ,
109- }))
111+ _LOGGER .info ("%s" , device .capabilities_dict ())
110112 else :
111113 # Enable energy requests
112114 if args .energy :
113- device ._request_energy_usage = True
115+ if hasattr (device , "enable_energy_usage_requests" ):
116+ device .enable_energy_usage_requests = True
117+ else :
118+ _LOGGER .error ("Device does not support energy data." )
119+ exit (1 )
114120
115121 _LOGGER .info ("Querying device state." )
116122 await device .refresh ()
@@ -136,22 +142,34 @@ def convert(v, t):
136142 v , t .__qualname__ )
137143 exit (1 )
138144
145+ # Create a dummy device instance for property validation
146+ device_type = DEVICE_TYPES .get (
147+ args .device_type , DeviceType .AIR_CONDITIONER )
148+ dummy_device = Device .construct (
149+ type = device_type ,
150+ ip = "0.0.0.0" ,
151+ device_id = 0 ,
152+ port = 6444
153+ )
154+ device_class = type (dummy_device )
155+
139156 # Parse each setting, checking if the property exists and the supplied value is valid
140157 new_properties = {}
141158 for name , value in (s .split ("=" ) for s in args .settings ):
142- # Check if property exists
143- prop = getattr (AC , name , None )
159+ # Check if property exists on the device class
160+ prop = getattr (device_class , name , None )
144161 if prop is None or not isinstance (prop , property ):
145- _LOGGER .error ("'%s' is not a valid device property." , name )
162+ _LOGGER .error (
163+ "'%s' is not a valid property for device type %02X." , name , device_type )
146164 exit (1 )
147165
148- # Check if property has a setter, with special handling for the display
149- if name != KEY_DISPLAY_ON and prop . fset is None :
166+ # Check if property has a setter, with special handling for the AC display
167+ if prop . fset is None and not ( device_class == AC and name == KEY_DISPLAY_ON ) :
150168 _LOGGER .error ("'%s' property is not writable." , name )
151169 exit (1 )
152170
153171 # Get the default value of the property and its type
154- attr_value = getattr (AC ( "0.0.0.0" , 0 , 0 ) , name )
172+ attr_value = getattr (dummy_device , name )
155173 attr_type = type (attr_value )
156174
157175 if isinstance (attr_value , MideaIntEnum ):
@@ -166,8 +184,9 @@ def convert(v, t):
166184 try :
167185 new_properties [name ] = attr_type (value )
168186 except ValueError :
169- # Allow raw integers for AC.FanSpeed
170- if attr_type == AC .FanSpeed :
187+ # Allow raw integers for FanSpeed if device supports custom fan speeds
188+ if (attr_type == device_class .FanSpeed and
189+ getattr (dummy_device , 'supports_custom_fan_speed' , False )):
171190 new_properties [name ] = int (value )
172191 else :
173192 _LOGGER .error ("Value '%d' is not a valid %s" ,
@@ -201,8 +220,9 @@ def convert(v, t):
201220 _LOGGER .info ("Querying device capabilities." )
202221 await device .get_capabilities ()
203222
204- # Handle display which is unique
205- if (display := new_properties .pop (KEY_DISPLAY_ON , None )) is not None :
223+ # Handle display which is unique to AC devices
224+ if ((display := new_properties .pop (KEY_DISPLAY_ON , None )) is not None
225+ and isinstance (device , AC )):
206226 if display != device .display_on :
207227 _LOGGER .info ("Setting '%s' to %s." , KEY_DISPLAY_ON , display )
208228 await device .toggle_display ()
@@ -233,6 +253,8 @@ async def _download(args) -> None:
233253
234254 if isinstance (device , AC ):
235255 device = super (AC , device )
256+ elif isinstance (device , CC ):
257+ device = super (CC , device )
236258
237259 _LOGGER .info ("Found device:\n %s" , device .to_dict ())
238260
@@ -338,8 +360,11 @@ def main() -> NoReturn:
338360 query_parser .add_argument ("--capabilities" ,
339361 help = "Query device capabilities instead of state." ,
340362 action = "store_true" )
363+ query_parser .add_argument ("--device_type" ,
364+ help = "Type of device." ,
365+ choices = DEVICE_TYPES .keys ())
341366 query_parser .add_argument ("--auto" ,
342- help = "Automatically authenticate V3 devices ." ,
367+ help = "Automatically identify, connect and authenticate with the device ." ,
343368 action = "store_true" )
344369 query_parser .add_argument ("--id" ,
345370 help = "Device ID for V3 devices." ,
@@ -364,8 +389,11 @@ def main() -> NoReturn:
364389 control_parser .add_argument ("--capabilities" ,
365390 help = "Query device capabilities before sending commands." ,
366391 action = "store_true" )
392+ control_parser .add_argument ("--device_type" ,
393+ help = "Type of device." ,
394+ choices = DEVICE_TYPES .keys ())
367395 control_parser .add_argument ("--auto" ,
368- help = "Automatically authenticate V3 devices ." ,
396+ help = "Automatically identify, connect and authenticate with the device ." ,
369397 action = "store_true" )
370398 control_parser .add_argument ("--id" ,
371399 help = "Device ID for V3 devices." ,
0 commit comments