77from aioesphomeapi import (
88 APIClient ,
99 APIVersion ,
10+ BluetoothDevicePairing ,
11+ BluetoothDeviceUnpairing ,
1012 BluetoothGATTCharacteristic ,
1113 BluetoothGATTDescriptor ,
1214 BluetoothGATTService ,
@@ -599,7 +601,7 @@ async def test_bleak_client_connect_with_pair_parameter(
599601 esphome_bluetooth_gatt_services : ESPHomeBluetoothGATTServices ,
600602 caplog : pytest .LogCaptureFixture ,
601603) -> None :
602- """Test connect with pair=True logs a warning ."""
604+ """Test connect with pair=True calls pair method ."""
603605 ble_device = generate_ble_device (
604606 "CC:BB:AA:DD:EE:FF" , details = {"source" : ESP_MAC_ADDRESS , "address_type" : 1 }
605607 )
@@ -619,6 +621,10 @@ async def test_bleak_client_connect_with_pair_parameter(
619621 "bluetooth_gatt_get_services" ,
620622 return_value = esphome_bluetooth_gatt_services ,
621623 ),
624+ patch .object (
625+ client ,
626+ "_pair" ,
627+ ) as mock_pair ,
622628 ):
623629 # Test with pair=True
624630 task = asyncio .create_task (bleak_client .connect ())
@@ -629,8 +635,7 @@ async def test_bleak_client_connect_with_pair_parameter(
629635 await task
630636
631637 assert client .is_connected
632- assert "Explicit pairing during connect is not available in ESPHome" in caplog .text
633- assert "Use the pair() method after connecting if needed" in caplog .text
638+ mock_pair .assert_called_once ()
634639
635640 with patch .object (
636641 client ._client ,
@@ -685,3 +690,209 @@ async def test_esphome_client_connect_with_pair_false(
685690 await client .disconnect ()
686691
687692 mock_disconnect .assert_called_once ()
693+
694+
695+ @pytest .mark .asyncio
696+ async def test_pair_success (
697+ client_data : ESPHomeClientData ,
698+ ) -> None :
699+ """Test successful pairing."""
700+ ble_device = generate_ble_device (
701+ "CC:BB:AA:DD:EE:FF" , details = {"source" : ESP_MAC_ADDRESS , "address_type" : 1 }
702+ )
703+
704+ client = ESPHomeClient (ble_device , client_data = client_data )
705+ # Simulate connection
706+ client ._is_connected = True
707+
708+ # Enable pairing feature flag
709+ client ._feature_flags |= BluetoothProxyFeature .PAIRING .value
710+
711+ with patch .object (
712+ client ._client ,
713+ "bluetooth_device_pair" ,
714+ return_value = BluetoothDevicePairing (
715+ address = client ._address_as_int ,
716+ paired = True ,
717+ error = 0 ,
718+ ),
719+ ) as mock_pair :
720+ await client .pair ()
721+
722+ mock_pair .assert_called_once_with (client ._address_as_int )
723+
724+
725+ @pytest .mark .asyncio
726+ async def test_pair_failure (
727+ client_data : ESPHomeClientData ,
728+ ) -> None :
729+ """Test pairing failure."""
730+ ble_device = generate_ble_device (
731+ "CC:BB:AA:DD:EE:FF" , details = {"source" : ESP_MAC_ADDRESS , "address_type" : 1 }
732+ )
733+
734+ client = ESPHomeClient (ble_device , client_data = client_data )
735+ # Simulate connection
736+ client ._is_connected = True
737+
738+ # Enable pairing feature flag
739+ client ._feature_flags |= BluetoothProxyFeature .PAIRING .value
740+
741+ with patch .object (
742+ client ._client ,
743+ "bluetooth_device_pair" ,
744+ return_value = BluetoothDevicePairing (
745+ address = client ._address_as_int ,
746+ paired = False ,
747+ error = 1 ,
748+ ),
749+ ):
750+ with pytest .raises (BleakError ) as exc_info :
751+ await client .pair ()
752+ assert "Pairing failed due to error: 1" in str (exc_info .value )
753+
754+
755+ @pytest .mark .asyncio
756+ async def test_pair_not_connected (
757+ client_data : ESPHomeClientData ,
758+ ) -> None :
759+ """Test pairing when not connected."""
760+ ble_device = generate_ble_device (
761+ "CC:BB:AA:DD:EE:FF" , details = {"source" : ESP_MAC_ADDRESS , "address_type" : 1 }
762+ )
763+
764+ client = ESPHomeClient (ble_device , client_data = client_data )
765+ # Device is not connected
766+ client ._is_connected = False
767+
768+ # Enable pairing feature flag
769+ client ._feature_flags |= BluetoothProxyFeature .PAIRING .value
770+
771+ with pytest .raises (BleakError ) as exc_info :
772+ await client .pair ()
773+ assert "is not connected" in str (exc_info .value )
774+
775+
776+ @pytest .mark .asyncio
777+ async def test_pair_feature_not_supported (
778+ client_data : ESPHomeClientData ,
779+ ) -> None :
780+ """Test pairing when feature is not supported."""
781+ ble_device = generate_ble_device (
782+ "CC:BB:AA:DD:EE:FF" , details = {"source" : ESP_MAC_ADDRESS , "address_type" : 1 }
783+ )
784+
785+ client = ESPHomeClient (ble_device , client_data = client_data )
786+ # Simulate connection
787+ client ._is_connected = True
788+
789+ # Disable pairing feature flag
790+ client ._feature_flags &= ~ BluetoothProxyFeature .PAIRING .value
791+
792+ with pytest .raises (NotImplementedError ) as exc_info :
793+ await client .pair ()
794+ assert "Pairing is not available in this version ESPHome" in str (exc_info .value )
795+ assert client ._device_info .name in str (exc_info .value )
796+
797+
798+ @pytest .mark .asyncio
799+ async def test_unpair_success (
800+ client_data : ESPHomeClientData ,
801+ ) -> None :
802+ """Test successful unpairing."""
803+ ble_device = generate_ble_device (
804+ "CC:BB:AA:DD:EE:FF" , details = {"source" : ESP_MAC_ADDRESS , "address_type" : 1 }
805+ )
806+
807+ client = ESPHomeClient (ble_device , client_data = client_data )
808+ # Simulate connection
809+ client ._is_connected = True
810+
811+ # Enable pairing feature flag
812+ client ._feature_flags |= BluetoothProxyFeature .PAIRING .value
813+
814+ with patch .object (
815+ client ._client ,
816+ "bluetooth_device_unpair" ,
817+ return_value = BluetoothDeviceUnpairing (
818+ address = client ._address_as_int ,
819+ success = True ,
820+ error = 0 ,
821+ ),
822+ ) as mock_unpair :
823+ await client .unpair ()
824+
825+ mock_unpair .assert_called_once_with (client ._address_as_int )
826+
827+
828+ @pytest .mark .asyncio
829+ async def test_unpair_failure (
830+ client_data : ESPHomeClientData ,
831+ ) -> None :
832+ """Test unpairing failure."""
833+ ble_device = generate_ble_device (
834+ "CC:BB:AA:DD:EE:FF" , details = {"source" : ESP_MAC_ADDRESS , "address_type" : 1 }
835+ )
836+
837+ client = ESPHomeClient (ble_device , client_data = client_data )
838+ # Simulate connection
839+ client ._is_connected = True
840+
841+ # Enable pairing feature flag
842+ client ._feature_flags |= BluetoothProxyFeature .PAIRING .value
843+
844+ with patch .object (
845+ client ._client ,
846+ "bluetooth_device_unpair" ,
847+ return_value = BluetoothDeviceUnpairing (
848+ address = client ._address_as_int ,
849+ success = False ,
850+ error = 2 ,
851+ ),
852+ ):
853+ with pytest .raises (BleakError ) as exc_info :
854+ await client .unpair ()
855+ assert "Unpairing failed due to error: 2" in str (exc_info .value )
856+
857+
858+ @pytest .mark .asyncio
859+ async def test_unpair_not_connected (
860+ client_data : ESPHomeClientData ,
861+ ) -> None :
862+ """Test unpairing when not connected."""
863+ ble_device = generate_ble_device (
864+ "CC:BB:AA:DD:EE:FF" , details = {"source" : ESP_MAC_ADDRESS , "address_type" : 1 }
865+ )
866+
867+ client = ESPHomeClient (ble_device , client_data = client_data )
868+ # Device is not connected
869+ client ._is_connected = False
870+
871+ # Enable pairing feature flag
872+ client ._feature_flags |= BluetoothProxyFeature .PAIRING .value
873+
874+ with pytest .raises (BleakError ) as exc_info :
875+ await client .unpair ()
876+ assert "is not connected" in str (exc_info .value )
877+
878+
879+ @pytest .mark .asyncio
880+ async def test_unpair_feature_not_supported (
881+ client_data : ESPHomeClientData ,
882+ ) -> None :
883+ """Test unpairing when feature is not supported."""
884+ ble_device = generate_ble_device (
885+ "CC:BB:AA:DD:EE:FF" , details = {"source" : ESP_MAC_ADDRESS , "address_type" : 1 }
886+ )
887+
888+ client = ESPHomeClient (ble_device , client_data = client_data )
889+ # Simulate connection
890+ client ._is_connected = True
891+
892+ # Disable pairing feature flag
893+ client ._feature_flags &= ~ BluetoothProxyFeature .PAIRING .value
894+
895+ with pytest .raises (NotImplementedError ) as exc_info :
896+ await client .unpair ()
897+ assert "Unpairing is not available in this version ESPHome" in str (exc_info .value )
898+ assert client ._device_info .name in str (exc_info .value )
0 commit comments