from __future__ import annotations import re from aioesphomeapi.model import BluetoothGATTError from .api_pb2 import ( # type: ignore AlarmControlPanelCommandRequest, AlarmControlPanelStateResponse, BinarySensorStateResponse, BluetoothConnectionsFreeResponse, BluetoothDeviceClearCacheResponse, BluetoothDeviceConnectionResponse, BluetoothDevicePairingResponse, BluetoothDeviceRequest, BluetoothDeviceUnpairingResponse, BluetoothGATTErrorResponse, BluetoothGATTGetServicesDoneResponse, BluetoothGATTGetServicesRequest, BluetoothGATTGetServicesResponse, BluetoothGATTNotifyDataResponse, BluetoothGATTNotifyRequest, BluetoothGATTNotifyResponse, BluetoothGATTReadDescriptorRequest, BluetoothGATTReadRequest, BluetoothGATTReadResponse, BluetoothGATTWriteDescriptorRequest, BluetoothGATTWriteRequest, BluetoothGATTWriteResponse, BluetoothLEAdvertisementResponse, BluetoothLERawAdvertisementsResponse, ButtonCommandRequest, CameraImageRequest, CameraImageResponse, ClimateCommandRequest, ClimateStateResponse, ConnectRequest, ConnectResponse, CoverCommandRequest, CoverStateResponse, DateCommandRequest, DateStateResponse, DateTimeCommandRequest, DateTimeStateResponse, DeviceInfoRequest, DeviceInfoResponse, DisconnectRequest, DisconnectResponse, EventResponse, ExecuteServiceRequest, FanCommandRequest, FanStateResponse, GetTimeRequest, GetTimeResponse, HelloRequest, HelloResponse, HomeassistantServiceResponse, HomeAssistantStateResponse, LightCommandRequest, LightStateResponse, ListEntitiesAlarmControlPanelResponse, ListEntitiesBinarySensorResponse, ListEntitiesButtonResponse, ListEntitiesCameraResponse, ListEntitiesClimateResponse, ListEntitiesCoverResponse, ListEntitiesDateResponse, ListEntitiesDateTimeResponse, ListEntitiesDoneResponse, ListEntitiesEventResponse, ListEntitiesFanResponse, ListEntitiesLightResponse, ListEntitiesLockResponse, ListEntitiesMediaPlayerResponse, ListEntitiesNumberResponse, ListEntitiesRequest, ListEntitiesSelectResponse, ListEntitiesSensorResponse, ListEntitiesServicesResponse, ListEntitiesSirenResponse, ListEntitiesSwitchResponse, ListEntitiesTextResponse, ListEntitiesTextSensorResponse, ListEntitiesTimeResponse, ListEntitiesUpdateResponse, ListEntitiesValveResponse, LockCommandRequest, LockStateResponse, MediaPlayerCommandRequest, MediaPlayerStateResponse, NumberCommandRequest, NumberStateResponse, PingRequest, PingResponse, SelectCommandRequest, SelectStateResponse, SensorStateResponse, SirenCommandRequest, SirenStateResponse, SubscribeBluetoothConnectionsFreeRequest, SubscribeBluetoothLEAdvertisementsRequest, SubscribeHomeassistantServicesRequest, SubscribeHomeAssistantStateResponse, SubscribeHomeAssistantStatesRequest, SubscribeLogsRequest, SubscribeLogsResponse, SubscribeStatesRequest, SubscribeVoiceAssistantRequest, SwitchCommandRequest, SwitchStateResponse, TextCommandRequest, TextSensorStateResponse, TextStateResponse, TimeCommandRequest, TimeStateResponse, UnsubscribeBluetoothLEAdvertisementsRequest, UpdateCommandRequest, UpdateStateResponse, ValveCommandRequest, ValveStateResponse, VoiceAssistantAudio, VoiceAssistantEventResponse, VoiceAssistantRequest, VoiceAssistantResponse, VoiceAssistantTimerEventResponse, ) TWO_CHAR = re.compile(r".{2}") # Taken from esp_gatt_status_t in esp_gatt_defs.h ESPHOME_GATT_ERRORS = { -1: "Not connected", # Custom ESPHome error 1: "Invalid handle", 2: "Read not permitted", 3: "Write not permitted", 4: "Invalid PDU", 5: "Insufficient authentication", 6: "Request not supported", 7: "Invalid offset", 8: "Insufficient authorization", 9: "Prepare queue full", 10: "Attribute not found", 11: "Attribute not long", 12: "Insufficient key size", 13: "Invalid attribute length", 14: "Unlikely error", 15: "Insufficient encryption", 16: "Unsupported group type", 17: "Insufficient resources", 128: "Application error", 129: "Internal error", 130: "Wrong state", 131: "Database full", 132: "Busy", 133: "Error", 134: "Command started", 135: "Illegal parameter", 136: "Pending", 137: "Auth fail", 138: "More", 139: "Invalid configuration", 140: "Service started", 141: "Encrypted no mitm", 142: "Not encrypted", 143: "Congested", 144: "Duplicate registration", 145: "Already open", 146: "Cancel", 224: "Stack RSP", 225: "App RSP", 239: "Unknown error", 253: "CCC config error", 254: "Procedure already in progress", 255: "Out of range", } class APIConnectionError(Exception): pass class APIConnectionCancelledError(APIConnectionError): pass class InvalidAuthAPIError(APIConnectionError): pass class ResolveAPIError(APIConnectionError): pass class ProtocolAPIError(APIConnectionError): pass class RequiresEncryptionAPIError(ProtocolAPIError): pass class SocketAPIError(APIConnectionError): pass class SocketClosedAPIError(SocketAPIError): pass class HandshakeAPIError(APIConnectionError): pass class ConnectionNotEstablishedAPIError(APIConnectionError): pass class BadNameAPIError(APIConnectionError): """Raised when a name received from the remote but does not much the expected name.""" def __init__(self, msg: str, received_name: str) -> None: super().__init__(f"{msg}: received_name={received_name}") self.received_name = received_name class InvalidEncryptionKeyAPIError(HandshakeAPIError): def __init__( self, msg: str | None = None, received_name: str | None = None ) -> None: super().__init__(f"{msg}: received_name={received_name}") self.received_name = received_name class PingFailedAPIError(APIConnectionError): pass class TimeoutAPIError(APIConnectionError): pass class ReadFailedAPIError(APIConnectionError): pass class UnhandledAPIConnectionError(APIConnectionError): pass class BluetoothConnectionDroppedError(APIConnectionError): """Raised when a Bluetooth connection is dropped.""" def to_human_readable_address(address: int) -> str: """Convert a MAC address to a human readable format.""" return ":".join(TWO_CHAR.findall(f"{address:012X}")) def to_human_readable_gatt_error(error: int) -> str: """Convert a GATT error to a human readable format.""" return ESPHOME_GATT_ERRORS.get(error, "Unknown error") class BluetoothGATTAPIError(APIConnectionError): def __init__(self, error: BluetoothGATTError) -> None: super().__init__( f"Bluetooth GATT Error " f"address={to_human_readable_address(error.address)} " f"handle={error.handle} " f"error={error.error} " f"description={to_human_readable_gatt_error(error.error)}" ) self.error = error MESSAGE_TYPE_TO_PROTO = { 1: HelloRequest, 2: HelloResponse, 3: ConnectRequest, 4: ConnectResponse, 5: DisconnectRequest, 6: DisconnectResponse, 7: PingRequest, 8: PingResponse, 9: DeviceInfoRequest, 10: DeviceInfoResponse, 11: ListEntitiesRequest, 12: ListEntitiesBinarySensorResponse, 13: ListEntitiesCoverResponse, 14: ListEntitiesFanResponse, 15: ListEntitiesLightResponse, 16: ListEntitiesSensorResponse, 17: ListEntitiesSwitchResponse, 18: ListEntitiesTextSensorResponse, 19: ListEntitiesDoneResponse, 20: SubscribeStatesRequest, 21: BinarySensorStateResponse, 22: CoverStateResponse, 23: FanStateResponse, 24: LightStateResponse, 25: SensorStateResponse, 26: SwitchStateResponse, 27: TextSensorStateResponse, 28: SubscribeLogsRequest, 29: SubscribeLogsResponse, 30: CoverCommandRequest, 31: FanCommandRequest, 32: LightCommandRequest, 33: SwitchCommandRequest, 34: SubscribeHomeassistantServicesRequest, 35: HomeassistantServiceResponse, 36: GetTimeRequest, 37: GetTimeResponse, 38: SubscribeHomeAssistantStatesRequest, 39: SubscribeHomeAssistantStateResponse, 40: HomeAssistantStateResponse, 41: ListEntitiesServicesResponse, 42: ExecuteServiceRequest, 43: ListEntitiesCameraResponse, 44: CameraImageResponse, 45: CameraImageRequest, 46: ListEntitiesClimateResponse, 47: ClimateStateResponse, 48: ClimateCommandRequest, 49: ListEntitiesNumberResponse, 50: NumberStateResponse, 51: NumberCommandRequest, 52: ListEntitiesSelectResponse, 53: SelectStateResponse, 54: SelectCommandRequest, 55: ListEntitiesSirenResponse, 56: SirenStateResponse, 57: SirenCommandRequest, 58: ListEntitiesLockResponse, 59: LockStateResponse, 60: LockCommandRequest, 61: ListEntitiesButtonResponse, 62: ButtonCommandRequest, 63: ListEntitiesMediaPlayerResponse, 64: MediaPlayerStateResponse, 65: MediaPlayerCommandRequest, 66: SubscribeBluetoothLEAdvertisementsRequest, 67: BluetoothLEAdvertisementResponse, 68: BluetoothDeviceRequest, 69: BluetoothDeviceConnectionResponse, 70: BluetoothGATTGetServicesRequest, 71: BluetoothGATTGetServicesResponse, 72: BluetoothGATTGetServicesDoneResponse, 73: BluetoothGATTReadRequest, 74: BluetoothGATTReadResponse, 75: BluetoothGATTWriteRequest, 76: BluetoothGATTReadDescriptorRequest, 77: BluetoothGATTWriteDescriptorRequest, 78: BluetoothGATTNotifyRequest, 79: BluetoothGATTNotifyDataResponse, 80: SubscribeBluetoothConnectionsFreeRequest, 81: BluetoothConnectionsFreeResponse, 82: BluetoothGATTErrorResponse, 83: BluetoothGATTWriteResponse, 84: BluetoothGATTNotifyResponse, 85: BluetoothDevicePairingResponse, 86: BluetoothDeviceUnpairingResponse, 87: UnsubscribeBluetoothLEAdvertisementsRequest, 88: BluetoothDeviceClearCacheResponse, 89: SubscribeVoiceAssistantRequest, 90: VoiceAssistantRequest, 91: VoiceAssistantResponse, 92: VoiceAssistantEventResponse, 93: BluetoothLERawAdvertisementsResponse, 94: ListEntitiesAlarmControlPanelResponse, 95: AlarmControlPanelStateResponse, 96: AlarmControlPanelCommandRequest, 97: ListEntitiesTextResponse, 98: TextStateResponse, 99: TextCommandRequest, 100: ListEntitiesDateResponse, 101: DateStateResponse, 102: DateCommandRequest, 103: ListEntitiesTimeResponse, 104: TimeStateResponse, 105: TimeCommandRequest, 106: VoiceAssistantAudio, 107: ListEntitiesEventResponse, 108: EventResponse, 109: ListEntitiesValveResponse, 110: ValveStateResponse, 111: ValveCommandRequest, 112: ListEntitiesDateTimeResponse, 113: DateTimeStateResponse, 114: DateTimeCommandRequest, 115: VoiceAssistantTimerEventResponse, 116: ListEntitiesUpdateResponse, 117: UpdateStateResponse, 118: UpdateCommandRequest, }