@Override
	public void onDataReceived(@NonNull final BluetoothDevice device, @NonNull final Data data) {
		super.onDataReceived(device, data);

		if (data.size() < 2) {
			onInvalidDataReceived(device, data);
			return;
		}

		// Read the Op Code
		final int opCode = data.getIntValue(Data.FORMAT_UINT8, 0);

		// Estimate the expected operand size based on the Op Code
		int expectedOperandSize;
		switch (opCode) {
			case OP_CODE_COMMUNICATION_INTERVAL_RESPONSE ->
				// UINT8
					expectedOperandSize = 1;
			case OP_CODE_CALIBRATION_VALUE_RESPONSE ->
				// Calibration Value
					expectedOperandSize = 10;
			case OP_CODE_PATIENT_HIGH_ALERT_LEVEL_RESPONSE,
				 OP_CODE_PATIENT_LOW_ALERT_LEVEL_RESPONSE,
				 OP_CODE_HYPO_ALERT_LEVEL_RESPONSE,
				 OP_CODE_HYPER_ALERT_LEVEL_RESPONSE,
				 OP_CODE_RATE_OF_DECREASE_ALERT_LEVEL_RESPONSE,
				 OP_CODE_RATE_OF_INCREASE_ALERT_LEVEL_RESPONSE ->
				// SFLOAT
					expectedOperandSize = 2;
			case OP_CODE_RESPONSE_CODE ->
				// Request Op Code (UINT8), Response Code Value (UINT8)
					expectedOperandSize = 2;
			default -> {
				onInvalidDataReceived(device, data);
				return;
			}
		}

		// Verify packet length
		if (data.size() != 1 + expectedOperandSize && data.size() != 1 + expectedOperandSize + 2) {
			onInvalidDataReceived(device, data);
			return;
		}

		// Verify CRC if present
		final boolean crcPresent = data.size() == 1 + expectedOperandSize + 2; // opCode + expected operand + CRC
		if (crcPresent) {
			final int expectedCrc = data.getIntValue(Data.FORMAT_UINT16_LE, 1 + expectedOperandSize);
			final int actualCrc   = CRC16.MCRF4XX(data.getValue(), 0, 1 + expectedOperandSize);
			if (expectedCrc != actualCrc) {
				onCGMSpecificOpsResponseReceivedWithCrcError(device, data);
				return;
			}
		}

		switch (opCode) {
			case OP_CODE_COMMUNICATION_INTERVAL_RESPONSE -> {
				final int interval = data.getIntValue(Data.FORMAT_UINT8, 1);
				onContinuousGlucoseCommunicationIntervalReceived(device, interval, crcPresent);
				return;
			}
			case OP_CODE_CALIBRATION_VALUE_RESPONSE -> {
				final float glucoseConcentrationOfCalibration = data.getFloatValue(Data.FORMAT_SFLOAT, 1);
				final int calibrationTime = data.getIntValue(Data.FORMAT_UINT16_LE, 3);
				final int calibrationTypeAndSampleLocation = data.getIntValue(Data.FORMAT_UINT8, 5);
				@SuppressLint("WrongConstant") final int calibrationType = calibrationTypeAndSampleLocation & 0x0F;
				final int calibrationSampleLocation = calibrationTypeAndSampleLocation >> 4;
				final int nextCalibrationTime = data.getIntValue(Data.FORMAT_UINT16_LE, 6);
				final int calibrationDataRecordNumber = data.getIntValue(Data.FORMAT_UINT16_LE, 8);
				final int calibrationStatus = data.getIntValue(Data.FORMAT_UINT8, 10);
				onContinuousGlucoseCalibrationValueReceived(device, glucoseConcentrationOfCalibration,
						calibrationTime, nextCalibrationTime, calibrationType, calibrationSampleLocation,
						calibrationDataRecordNumber, new CGMCalibrationStatus(calibrationStatus), crcPresent);
				return;
			}
			case OP_CODE_RESPONSE_CODE -> {
				final int requestCode = data.getIntValue(Data.FORMAT_UINT8, 1); // ignore
				final int responseCode = data.getIntValue(Data.FORMAT_UINT8, 2);
				if (responseCode == CGM_RESPONSE_SUCCESS) {
					onCGMSpecificOpsOperationCompleted(device, requestCode, crcPresent);
				} else {
					onCGMSpecificOpsOperationError(device, requestCode, responseCode, crcPresent);
				}
				return;
			}
		}

		// Read SFLOAT value
		final float value = data.getFloatValue(Data.FORMAT_SFLOAT, 1);
		switch (opCode) {
			case OP_CODE_PATIENT_HIGH_ALERT_LEVEL_RESPONSE ->
					onContinuousGlucosePatientHighAlertReceived(device, value, crcPresent);
			case OP_CODE_PATIENT_LOW_ALERT_LEVEL_RESPONSE ->
					onContinuousGlucosePatientLowAlertReceived(device, value, crcPresent);
			case OP_CODE_HYPO_ALERT_LEVEL_RESPONSE ->
					onContinuousGlucoseHypoAlertReceived(device, value, crcPresent);
			case OP_CODE_HYPER_ALERT_LEVEL_RESPONSE ->
					onContinuousGlucoseHyperAlertReceived(device, value, crcPresent);
			case OP_CODE_RATE_OF_DECREASE_ALERT_LEVEL_RESPONSE ->
					onContinuousGlucoseRateOfDecreaseAlertReceived(device, value, crcPresent);
			case OP_CODE_RATE_OF_INCREASE_ALERT_LEVEL_RESPONSE ->
					onContinuousGlucoseRateOfIncreaseAlertReceived(device, value, crcPresent);
		}
	}
@Test
	public void onContinuousGlucoseHyperAlertReceived() {
		final Data data = new Data(new byte[] { 18, 10, 32});
		callback.onDataReceived(null, data);
		assertEquals("Level", 1000f, hyperAlertLevel, 0.01);
		assertFalse(secured);
	}