Send Command - Bad Request

Macedoa year ago

Hi, I'm trying to send commands saved by api, but I always get BAD Request as a response. I already downloaded the sample app and managed to run it, but it has the same error.
This is the function:

          @POST("/api/commands/send")
          Call<Command> sendCommand(@Body Command command);

This is the json I send via post:

          {"attributes":{"frequency":5},"description":"5 sec","deviceId":143,"type":"positionPeriodic"}

This is the error I get:

          APIService.ServiceException: Bad Request

I'm asking for help, because I've already done all the tests and comparisons, but I haven't found the error.

Anton Tananaeva year ago

What's the detailed error message in the payload?

Macedoa year ago

In Android Studio, only "APIService.ServiceException: Bad Request"

This is the code (Traccar Manager example in git hub), I did tests pointing to https://demo4.traccar.org/ and got the same error.

public class SendCommandFragment extends Fragment {

    class CommandTypeDataHolder {
        private String type;
        private String name;

        public CommandTypeDataHolder(String type, String name) {
            this.type = type;
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    class CommandTypeAdapter extends ArrayAdapter<CommandTypeDataHolder> {

        CommandTypeAdapter(List<CommandTypeDataHolder> items) {
            super(getActivity(), R.layout.list_item, android.R.id.text1, items);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup container) {
            View view = super.getView(position, convertView, container);

            CommandTypeDataHolder commandTypeDataHolder = getItem(position);
            TextView popupText = (TextView) view.findViewById(android.R.id.text1);
            popupText.setText(commandTypeDataHolder.name);
            view.setTag(commandTypeDataHolder.type);

            return view;
        }
    }

    private static Map<String, Integer> i10nMapping = new HashMap<>();

    static {
        i10nMapping.put(Command.TYPE_ALARM_ARM, R.string.command_alarm_arm);
        i10nMapping.put(Command.TYPE_ALARM_DISARM, R.string.command_alarm_disarm);
        i10nMapping.put(Command.TYPE_ENGINE_STOP, R.string.command_engine_stop);
        i10nMapping.put(Command.TYPE_ENGINE_RESUME, R.string.command_engine_resume);
        i10nMapping.put(Command.TYPE_POSITION_PERIODIC, R.string.command_position_periodic);
    }

    public static final String EXTRA_DEVICE_ID = "deviceId";

    private Spinner commandsSpinner;
    private LinearLayout frequencyGroup;
    private EditText frequencyEditText;
    private LinearLayout unitGroup;
    private Spinner unitSpinner;
    private View sendButton;

    @Nullable
    @Override
    public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_send_command, container, false);

        commandsSpinner = (Spinner) view.findViewById(R.id.commands);
        frequencyGroup = (LinearLayout) view.findViewById(R.id.frequencyGroup);
        frequencyEditText = (EditText) view.findViewById(R.id.frequency);
        unitGroup = (LinearLayout) view.findViewById(R.id.unitGroup);
        unitSpinner = (Spinner) view.findViewById(R.id.unit);
        sendButton = (Button) view.findViewById(R.id.button_send);

        frequencyGroup.setVisibility(View.GONE);
        unitGroup.setVisibility(View.GONE);

        commandsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if(view.getTag().toString().equals(Command.TYPE_POSITION_PERIODIC)) {
                    frequencyGroup.setVisibility(View.VISIBLE);
                    unitGroup.setVisibility(View.VISIBLE);
                } else {
                    frequencyGroup.setVisibility(View.GONE);
                    unitGroup.setVisibility(View.GONE);
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
                frequencyGroup.setVisibility(View.GONE);
                unitGroup.setVisibility(View.GONE);
            }
        });

        final long deviceId = getActivity().getIntent().getExtras().getLong(EXTRA_DEVICE_ID);

        final MainApplication application = (MainApplication) getActivity().getApplication();
        final WebService service = application.getService();
        service.putCommands(deviceId).enqueue(new WebServiceCallback<List<CommandType>>(getContext()) {
            @Override
            public void onSuccess(Response<List<CommandType>> response) {
                List<CommandTypeDataHolder> commandTypeDataHolders = new ArrayList<>();

                for (CommandType commandType: response.body()) {
                    String name = getI10nString(commandType.getType());
                    commandTypeDataHolders.add(new CommandTypeDataHolder(commandType.getType(), name));
                }

                commandsSpinner.setAdapter(new CommandTypeAdapter(commandTypeDataHolders));
            }
        });

        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Command command = new Command();
                command.setDeviceId(deviceId);
                command.setType((String) commandsSpinner.getSelectedView().getTag());

                if (command.getType().equals(Command.TYPE_POSITION_PERIODIC)) {
                    int value = 0;
                    try {
                        value = Integer.parseInt(frequencyEditText.getText().toString());
                    } catch (NumberFormatException e) {
                        Toast.makeText(getContext(), R.string.error_invalid_frequency, Toast.LENGTH_LONG).show();
                        return;
                    }
                    int multiplier = getResources().getIntArray(R.array.unit_values)[unitSpinner.getSelectedItemPosition()];
                    command.set(Command.KEY_FREQUENCY, value * multiplier);
                }

                final MainApplication application = (MainApplication) getActivity().getApplication();
                final WebService service = application.getService();
                service.sendCommand(command).enqueue(new WebServiceCallback<Command>(getContext()) {
                    @Override
                    public void onSuccess(Response<Command> response) {
                        Toast.makeText(getContext(), R.string.command_sent, Toast.LENGTH_LONG).show();
                    }

                    @Override
                    public void onFailure(Call<Command> call, Throwable t) {
                        super.onFailure(call, t);
                        Toast.makeText(getContext(), (new Gson().toJson(command) + "\n" + t.toString()),Toast.LENGTH_LONG).show();
                    }

                });

                getActivity().finish();
            }
        });

        return view;
    }

    private String getI10nString(String key) {
        String result = key;

        Integer resId = i10nMapping.get(key);
        if(resId != null) {
            try {
                CharSequence nameCharSequence = getContext().getResources().getText(resId);
                result = nameCharSequence.toString();
            } catch (Resources.NotFoundException e) {
                Log.w(SendCommandFragment.class.getSimpleName(), e);
            }
        }

        return result;
    }
}
Anton Tananaeva year ago

You have to check the payload.

Macedoa year ago

Hello, I don't understand, my payload is:

 {"attributes":{"frequency":5},"description":"5 sec","deviceId":143,"type":"positionPeriodic"}

I get this same bad request error in the traccar-manager-android-native app. I am not able to find the error in the tracer log for more information and in the debug mode of Android Studio neither. Is there a way to activate the more complete log to identify the error, or can you give me some more guidance? Thanks

Anton Tananaeva year ago

I'm asking for response payload, not the request. I'm not asking for logs either.

Macedoa year ago

I didn't get the payload of the response, I just get this in onFailure(Call<Command> call, Throwable t):
.APIService.ServiceException: Bad Request

Anton Tananaeva year ago

There is always a payload if there's a "Bad Request" error. If you don't know how to see the payload, you should read the documentation on the tool you're using to send API requests and figure out how to do it. Once you know the payload, we can continue this conversation.

Macedoa year ago

Hello, thanks to you I just learned a little more about Android Studio. What are you asking for this?

Account has limit sending commands - SecurityException (PermissionsManager:290 < CommandResource: 69 < ...)
Anton Tananaeva year ago

It means what it says. Commands are restricted for the account or the server.

Macedoa year ago

One more piece of information, with the same user logged in via the browser, the command is sent normally.

Macedoa year ago

please, be clearer, I did not find this information that you posted, much less understood what you meant, especially when the same user manages to send the commands through the browser, but logged in through the app it does not send them. When you say restricted to the server, I understood even less and searching the traccar forum and the documentation, I didn't find anything about it. Thank you very much for your time.

Anton Tananaeva year ago

There must be some difference.

Hello!
Bad request means that the server does not recognize the command because it is missing necessary data, or it is not in the correct format or data is missing.

try this way:

{id: 0, attributes: {"frequency":5}, deviceId: 143, type: "positionPeriodic", textChannel: false, description: "5 sec"}
  • A good tip when you have doubts about the API is to perform the process in Traccar WEB and inspect the output.
Macedoa year ago

Problem solved.

When sending the command requesting the saved commands for the desired device, as a response I get:

{
  "id": 7,
  "attributes": {
    "frequency": 5
  },
  "deviceId": 128,
  "type": "positionPeriodic",
  "textChannel": false,
  "description": "5 sec"
}

[
  {
    "id": 7,
    "attributes": {
      "frequency": 5
    },
    "deviceId": 0,
    "type": "positionPeriodic",
    "textChannel": false,
    "description": "5 sec"
  },
  {
    "id": 8,
    "attributes": {
      "frequency": 10
    },
    "deviceId": 0,
    "type": "positionPeriodic",
    "textChannel": false,
    "description": "10 sec"
  },
  {
    "id": 9,
    "attributes": {
      "frequency": 15
    },
    "deviceId": 0,
    "type": "positionPeriodic",
    "textChannel": false,
    "description": "15 sec"
  }
]

The id field needs to be saved to be sent correctly later.
The code had to be changed to store the id and when sending the command to be executed, send it with the same id, getting my request like this:

{
  "id": 7,
  "attributes": {
    "frequency": 5
  },
  "deviceId": 130,
  "type": "positionPeriodic",
  "textChannel": false,
  "description": "5 sec"
}

If you wish, I can send you the Traccar Manager Native code corrected with this implementation and working in the new version of android studio. Just let me know how to send it to you.