1111# String generation
1212import random
1313import string
14+ from unittest .mock import patch
1415
1516# Testing
1617import aiohttp
@@ -54,6 +55,7 @@ async def setup_app(
5455 timeout = False ,
5556 raise_timeout = False ,
5657 fail_auth = False ,
58+ stretch = False ,
5759 ):
5860 """Create mock webserver for Smile to interface with."""
5961 app = aiohttp .web .Application ()
@@ -86,12 +88,17 @@ async def setup_app(
8688 "DELETE" , "/core/notifications{tail:.*}" , self .smile_del_notification
8789 )
8890 app .router .add_route ("PUT" , "/core/rules{tail:.*}" , self .smile_set_schedule )
89- app .router .add_route (
90- "PUT" , "/core/appliances{tail:.*}" , self .smile_set_relay
91- )
9291 app .router .add_route (
9392 "DELETE" , "/core/notifications{tail:.*}" , self .smile_del_notification
9493 )
94+ if not stretch :
95+ app .router .add_route (
96+ "PUT" , "/core/appliances{tail:.*}" , self .smile_set_relay
97+ )
98+ else :
99+ app .router .add_route (
100+ "PUT" , "/core/appliances{tail:.*}" , self .smile_set_relay_stretch
101+ )
95102 else :
96103 app .router .add_route ("PUT" , "/core/locations{tail:.*}" , self .smile_timeout )
97104 app .router .add_route ("PUT" , "/core/rules{tail:.*}" , self .smile_timeout )
@@ -176,6 +183,11 @@ async def smile_set_relay(self, request):
176183 text = "<xml />"
177184 raise aiohttp .web .HTTPAccepted (text = text )
178185
186+ async def smile_set_relay_stretch (self , request ):
187+ """Render generic API calling endpoint."""
188+ text = "<xml />"
189+ raise aiohttp .web .HTTPOk (text = text )
190+
179191 async def smile_del_notification (self , request ):
180192 """Render generic API calling endpoint."""
181193 text = "<xml />"
@@ -194,13 +206,18 @@ async def smile_fail_auth(self, request):
194206 raise aiohttp .web .HTTPUnauthorized ()
195207
196208 async def connect (
197- self , broken = False , timeout = False , raise_timeout = False , fail_auth = False
209+ self ,
210+ broken = False ,
211+ timeout = False ,
212+ raise_timeout = False ,
213+ fail_auth = False ,
214+ stretch = False ,
198215 ):
199216 """Connect to a smile environment and perform basic asserts."""
200217 port = aiohttp .test_utils .unused_port ()
201218
202219 # Happy flow
203- app = await self .setup_app (broken , timeout , raise_timeout , fail_auth )
220+ app = await self .setup_app (broken , timeout , raise_timeout , fail_auth , stretch )
204221
205222 server = aiohttp .test_utils .TestServer (
206223 app , port = port , scheme = "http" , host = "127.0.0.1"
@@ -271,7 +288,9 @@ async def connect(
271288 raise exception
272289
273290 # Wrap connect for invalid connections
274- async def connect_wrapper (self , raise_timeout = False , fail_auth = False ):
291+ async def connect_wrapper (
292+ self , raise_timeout = False , fail_auth = False , stretch = False
293+ ):
275294 """Wrap connect to try negative testing before positive testing."""
276295
277296 if fail_auth :
@@ -305,7 +324,7 @@ async def connect_wrapper(self, raise_timeout=False, fail_auth=False):
305324 _LOGGER .info (" + successfully passed XML issue handling." )
306325
307326 _LOGGER .info ("Connecting to functioning device:" )
308- return await self .connect ()
327+ return await self .connect (stretch = stretch )
309328
310329 # Generic disconnect
311330 @pytest .mark .asyncio
@@ -351,6 +370,7 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None):
351370 pp4 = PrettyPrinter (indent = 4 )
352371 pp8 = PrettyPrinter (indent = 8 )
353372 _LOGGER .debug ("Device list:\n %s" , pp4 .pformat (device_list ))
373+ await smile .update_device ()
354374 for dev_id , details in device_list .items ():
355375 data = smile .get_device_data (dev_id )
356376 self ._write_json ("get_device_data/" + dev_id , data )
@@ -388,29 +408,28 @@ async def device_test(self, smile=pw_smile.Smile, testdata=None):
388408
389409 @pytest .mark .asyncio
390410 async def tinker_switch (
391- self , smile , dev_ids = None , members = None , model = None , unhappy = False
411+ self , smile , dev_id = None , members = None , model = None , unhappy = False
392412 ):
393413 """Turn a Switch on and off to test functionality."""
394414 _LOGGER .info ("Asserting modifying settings for switch devices:" )
395- for dev_id in dev_ids :
396- _LOGGER .info ("- Devices (%s):" , dev_id )
397- for new_state in [False , True , False ]:
398- _LOGGER .info ("- Switching %s" , new_state )
399- try :
400- switch_change = await smile .set_switch_state (
401- dev_id , members , model , new_state
402- )
403- assert switch_change
404- _LOGGER .info (" + worked as intended" )
405- except (
406- pw_exceptions .ErrorSendingCommandError ,
407- pw_exceptions .ResponseError ,
408- ):
409- if unhappy :
410- _LOGGER .info (" + failed as expected" )
411- else : # pragma: no cover
412- _LOGGER .info (" - failed unexpectedly" )
413- raise self .UnexpectedError
415+ _LOGGER .info ("- Devices (%s):" , dev_id )
416+ for new_state in [False , True , False ]:
417+ _LOGGER .info ("- Switching %s" , new_state )
418+ try :
419+ switch_change = await smile .set_switch_state (
420+ dev_id , members , model , new_state
421+ )
422+ assert switch_change
423+ _LOGGER .info (" + worked as intended" )
424+ except (
425+ pw_exceptions .ErrorSendingCommandError ,
426+ pw_exceptions .ResponseError ,
427+ ):
428+ if unhappy :
429+ _LOGGER .info (" + failed as expected" )
430+ else : # pragma: no cover
431+ _LOGGER .info (" - failed unexpectedly" )
432+ raise self .UnexpectedError
414433
415434 @pytest .mark .asyncio
416435 async def tinker_thermostat (self , smile , loc_id , good_schemas = None , unhappy = False ):
@@ -799,7 +818,7 @@ async def test_connect_anna_without_boiler_fw3(self):
799818 "a270735e4ccd45239424badc0578a2b1" : {
800819 "outdoor_temperature" : 10.8 ,
801820 },
802- ## Central
821+ # # Central
803822 # "c46b4794d28149699eacf053deedd003": {
804823 # "heating_state": False,
805824 # },
@@ -858,7 +877,7 @@ async def test_connect_anna_without_boiler_fw4(self):
858877 "a270735e4ccd45239424badc0578a2b1" : {
859878 "outdoor_temperature" : 16.6 ,
860879 },
861- ## Central
880+ # # Central
862881 # "c46b4794d28149699eacf053deedd003": {
863882 # "heating_state": True,
864883 # },
@@ -948,7 +967,7 @@ async def test_connect_adam_plus_anna(self):
948967 await self .tinker_thermostat (
949968 smile , "009490cc2f674ce6b576863fbb64f867" , good_schemas = ["Weekschema" ]
950969 )
951- await self .tinker_switch (smile , [ "aa6b0002df0a46e1b1eb94beb61eddfe" ] )
970+ await self .tinker_switch (smile , "aa6b0002df0a46e1b1eb94beb61eddfe" )
952971 await smile .close_connection ()
953972 await self .disconnect (server , client )
954973
@@ -960,7 +979,7 @@ async def test_connect_adam_plus_anna(self):
960979 unhappy = True ,
961980 )
962981 await self .tinker_switch (
963- smile , [ "aa6b0002df0a46e1b1eb94beb61eddfe" ] , unhappy = True
982+ smile , "aa6b0002df0a46e1b1eb94beb61eddfe" , unhappy = True
964983 )
965984 await smile .close_connection ()
966985 await self .disconnect (server , client )
@@ -992,11 +1011,11 @@ async def test_connect_adam_plus_anna_new(self):
9921011
9931012 await self .tinker_switch (
9941013 smile ,
995- [ "b83f9f9758064c0fab4af6578cba4c6d" ] ,
1014+ "b83f9f9758064c0fab4af6578cba4c6d" ,
9961015 ["aa6b0002df0a46e1b1eb94beb61eddfe" , "f2be121e4a9345ac83c6e99ed89a98be" ],
9971016 )
9981017 await self .tinker_switch (
999- smile , [ "2743216f626f43948deec1f7ab3b3d70" ] , model = "dhw_cm_switch"
1018+ smile , "2743216f626f43948deec1f7ab3b3d70" , model = "dhw_cm_switch"
10001019 )
10011020
10021021 await smile .close_connection ()
@@ -1068,7 +1087,7 @@ async def test_connect_adam_zone_per_device(self):
10681087 await self .tinker_thermostat (
10691088 smile , "82fa13f017d240daa0d0ea1775420f24" , good_schemas = ["CV Jessie" ]
10701089 )
1071- await self .tinker_switch (smile , [ "675416a629f343c495449970e2ca37b5" ] )
1090+ await self .tinker_switch (smile , "675416a629f343c495449970e2ca37b5" )
10721091 await smile .close_connection ()
10731092 await self .disconnect (server , client )
10741093
@@ -1162,7 +1181,7 @@ async def test_connect_adam_multiple_devices_per_zone(self):
11621181 await self .tinker_thermostat (
11631182 smile , "82fa13f017d240daa0d0ea1775420f24" , good_schemas = ["CV Jessie" ]
11641183 )
1165- await self .tinker_switch (smile , [ "675416a629f343c495449970e2ca37b5" ] )
1184+ await self .tinker_switch (smile , "675416a629f343c495449970e2ca37b5" )
11661185 await smile .close_connection ()
11671186 await self .disconnect (server , client )
11681187
@@ -1413,7 +1432,7 @@ async def test_connect_stretch_v31(self):
14131432 }
14141433
14151434 self .smile_setup = "stretch_v31"
1416- server , smile , client = await self .connect_wrapper ()
1435+ server , smile , client = await self .connect_wrapper (stretch = True )
14171436 assert smile .smile_hostname == "stretch000000"
14181437
14191438 _LOGGER .info ("Basics:" )
@@ -1449,7 +1468,7 @@ async def test_connect_stretch_v23(self):
14491468 }
14501469
14511470 self .smile_setup = "stretch_v23"
1452- server , smile , client = await self .connect_wrapper ()
1471+ server , smile , client = await self .connect_wrapper (stretch = True )
14531472 assert smile .smile_hostname == "stretch000000"
14541473
14551474 _LOGGER .info ("Basics:" )
@@ -1462,7 +1481,12 @@ async def test_connect_stretch_v23(self):
14621481 _LOGGER .info (" # Assert no master thermostat" )
14631482 assert smile .single_master_thermostat () is None # it's not a thermostat :)
14641483
1465- await self .tinker_switch (smile , ["2587a7fcdd7e482dab03fda256076b4b" ])
1484+ await self .tinker_switch (smile , "2587a7fcdd7e482dab03fda256076b4b" )
1485+ await self .tinker_switch (
1486+ smile ,
1487+ "f7b145c8492f4dd7a4de760456fdef3e" ,
1488+ ["407aa1c1099d463c9137a3a9eda787fd" ],
1489+ )
14661490
14671491 smile .get_all_devices ()
14681492 await self .device_test (smile , testdata )
@@ -1535,6 +1559,17 @@ async def test_connect_fail_firmware(self):
15351559 except pw_exceptions .UnsupportedDeviceError :
15361560 assert True
15371561
1562+ # Test connect for timeout
1563+ @patch ("async_timeout.timeout" , side_effect = asyncio .exceptions .TimeoutError )
1564+ async def test_connect_timeout (self , timeout_test ):
1565+ """Wrap connect to raise timeout during get."""
1566+
1567+ try :
1568+ await self .connect_wrapper ()
1569+ assert False # pragma: no cover
1570+ except pw_exceptions .DeviceTimeoutError :
1571+ assert True
1572+
15381573 class PlugwiseTestError (Exception ):
15391574 """Plugwise test exceptions class."""
15401575
0 commit comments