API calls from an android app to controll devices with similar functionality to 3D printers

I've created an Android app (written in java) to control the functions of a Kinetic Sand Table, which is based with its functionality on OctoPrint with exclusion of the z axis.

To make sure everything would work prior to hooking up the the SandTable, I decided to start off with attaching the 3D printer. That didn't work because the printer I have is already hooked.

So change of plans, now I tried using the Virtual Printer option. and i'm running OctoPI

What is the problem?

my app is not making API calls!
I'm figuring, something wrong with how I'm trying to access the endpoints!

this is the app code:

public class MainActivity extends AppCompatActivity {

//String url = "http://octopiaddress/api/job/start?apikey=mynoneglobalapikey";

private static final String OctoPrint_IP = "octopiaddress";
private static final String baseUrl = OctoPrint_IP + "/api";
private static final String jobUrl = baseUrl + "/job";
private static final String API_KEY = "mynoneglobalapikey";
private final String TAG = "tag123";
private Map<String, String> headers;
Button playButton;
Button pauseButton;
Button stopButton;
Button restartButton;
//Button settingsButton;
Spinner patternSpinner;
SeekBar colorSeekBar;
SeekBar brightnessSeekBar;

// URLs for the OctoPrint APIs
//private static final String settingsUrl = baseUrl + "job/settings?apikey=" + API_KEY;
private static final String urlLedControl = "http://octopi.local/plugin/ledcontrol?apikey=" + API_KEY;
private static final String urlColor = "http://octopi.local/plugin/colorControl?apikey=" + API_KEY;
private static final String files = baseUrl + "/files/local" + API_KEY;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // set the theme to no title bar
    setTheme(android.R.style.Theme_NoTitleBar);
    // set the content view for your activity
    setContentView(R.layout.activity_main);

    playButton = findViewById(R.id.play_button);
    pauseButton = findViewById(R.id.pause_button);
    stopButton = findViewById(R.id.stop_button);
    restartButton = findViewById(R.id.restart_button);
    //settingsButton = findViewById(R.id.settings_button);
    colorSeekBar = findViewById(R.id.color);
    brightnessSeekBar = findViewById(R.id.brightness);

    patternSpinner = findViewById(R.id.spinner);

    headers = new HashMap<>();
    headers.put("Content-Type", "application/json");
    headers.put("X-Api-Key", API_KEY);

    playButton.setOnClickListener(v -> startPrintJob());

    // Set the pause/resume click listener
    pauseButton.setOnClickListener(new View.OnClickListener() {
        boolean paused = false;

        @Override
        public void onClick(View v) {
            if (paused) {
                pausePrintJob();
                paused = false;
            } else {
                pausePrintJob();
                paused = true;
            }
        }
    });

    stopButton.setOnClickListener(v -> stopPrintJob());

    restartButton.setOnClickListener(v -> restartPrintJob());

    //settingsButton.setOnClickListener(v -> sendOctoprintRequest(settingsUrl));

    // The URL to retrieve the patterns from the OctoPrint API
    //String filesUrl = "http://octopiaddress/api/files/local?apikey=mynoneglobalapikey";
    // Make the HTTP request to retrieve the patterns from the OctoPrint API
    JsonArrayRequest request = new JsonArrayRequest(
            Request.Method.GET,
            files,
            null,
            response -> {
                // Log the response received
                Log.d(TAG, "Response received: " + response.toString());
                // Parse the JSON response and add the patterns to the spinner
                List<String> patternNames = new ArrayList<>();
                for (int i = 0; i < response.length(); i++) {
                    try {
                        JSONObject pattern = response.getJSONObject(i);
                        String name = pattern.getString("pattern");
                        patternNames.add(name);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
                // Creating adapter for spinner
                ArrayAdapter<String> adapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_spinner_item, patternNames);
                // Drop down layout style - list view with radio button
                adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                // attaching data adapter to spinner
                patternSpinner.setAdapter(adapter);
                Log.d(TAG, "HTTP request successful");

            },
            this::handleVolleyError

    );

    // Add the request to the Volley request queue
    RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
    queue.add(request);

    // Set a listener for when a pattern is selected in the spinner
    patternSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            String selectedPatternName = (String) parent.getItemAtPosition(position);

            // Construct the JSON object to start the pattern
            JSONObject jsonBody = new JSONObject();
            try {
                jsonBody.put("name", selectedPatternName);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            // Log the JSON body being sent in the request
            Log.d(TAG, "Sending JSON body in the request: " + jsonBody);
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
            // Not used in this example
        }
    });

    brightnessSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar brightnessSeekBar, int progress, boolean fromUser) {
            // Calculate the brightness percentage (0-100) from the seek bar progress (0-255)
            int brightnessPercent = (int) (progress * 100 / 255.0);

            // Construct the JSON object to set the brightness
            JSONObject jsonBody = new JSONObject();
            try {
                jsonBody.put("command", "ledcontrol");
                jsonBody.put("brightness", brightnessPercent);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            // Log the JSON body being sent in the request
            Log.d(TAG, "Sending JSON body in the request: " + jsonBody);

            // Send the HTTP request using Volley
            JsonObjectRequest request = new JsonObjectRequest(
                    Request.Method.POST,
                    urlLedControl,
                    jsonBody,
                    response -> {
                        // Log the response received
                        Log.d(TAG, "Response received: " + response.toString());
                        // Handle the response from the OctoPrint API
                        try {
                            boolean success = response.getBoolean("success");
                            if (success) {
                                Toast.makeText(getApplicationContext(), "brightness set successfully", Toast.LENGTH_SHORT).show();
                            } else {
                                Toast.makeText(getApplicationContext(), "Failed to set brightness", Toast.LENGTH_SHORT).show();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    },
                    error -> handleVolleyError(error)

            );
            // Add the request to the Volley request queue
            RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
            queue.add(request);
        }

        @Override
        public void onStartTrackingTouch(SeekBar brightnessSeekBar) {
            // Not used in this example
        }

        @Override
        public void onStopTrackingTouch(SeekBar brightnessSeekBar) {
            // Not used in this example
        }
    });


    colorSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar colorSeekBar, int progress, boolean fromUser) {
            // Make HTTP requests to the OctoPrint API to adjust the color of the lights
            JSONObject jsonBody = new JSONObject();
            try {
                jsonBody.put("color", progress);
            } catch (JSONException e) {
                e.printStackTrace();
            }

            JsonObjectRequest request = new JsonObjectRequest(
                    Request.Method.POST,
                    urlColor,
                    jsonBody,
                    response -> {
                        // Handle the response from the API
                        try {
                            boolean success = response.getBoolean("success");
                            if (success) {
                                Toast.makeText(getApplicationContext(), "Color set successfully", Toast.LENGTH_SHORT).show();
                            } else {
                                Toast.makeText(getApplicationContext(), "Failed to set color", Toast.LENGTH_SHORT).show();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    },
                    error -> handleVolleyError(error)
            );

            queue.add(request);
        }

        @Override
        public void onStartTrackingTouch(SeekBar colorSeekBar) {
            // Not used in this example
        }

        @Override
        public void onStopTrackingTouch(SeekBar colorSeekBar) {
            // Not used in this example
        }
    });
}

private void startPrintJob() {
    JSONObject jsonObject = new JSONObject();
    try {
        jsonObject.put("command", "start");
    } catch (JSONException e) {
        e.printStackTrace();
        return;
    }

    JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, jobUrl, jsonObject,
            response -> Toast.makeText(MainActivity.this, "Print started", Toast.LENGTH_SHORT).show(), error -> Toast.makeText(MainActivity.this, "Failed to start print", Toast.LENGTH_SHORT).show()) {
        @Override
        public Map<String, String> getHeaders() {
            return headers;
        }
    };

    Volley.newRequestQueue(this).add(request);
}

// private void startPrintJob() {
// RequestQueue requestQueue = Volley.newRequestQueue(this);
// StringRequest stringRequest = new StringRequest(
// Request.Method.POST,
// jobUrl,
// response -> Toast.makeText(MainActivity.this, "Print started", Toast.LENGTH_SHORT).show(), error -> Toast.makeText(MainActivity.this, "Failed to start print", Toast.LENGTH_SHORT).show()) {
// @Override
// protected Map<String, String> getParams() {
// // Add any parameters needed for the API call
// Map<String, String> params = new HashMap<>();
// params.put("command", "start");
// return params;
// }
//
// @Override
// public Map<String, String> getHeaders() {
// return headers;
// }
// };
// requestQueue.add(stringRequest);
// }

private void pausePrintJob() {
    JSONObject jsonObject = new JSONObject();
    try {
        jsonObject.put("command", "pause");
    } catch (JSONException e) {
        e.printStackTrace();
        return;
    }

    JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, jobUrl, jsonObject,
            response -> Toast.makeText(MainActivity.this, "Print paused", Toast.LENGTH_SHORT).show(), error -> Toast.makeText(MainActivity.this, "Failed to pause print", Toast.LENGTH_SHORT).show()) {
        @Override
        public Map<String, String> getHeaders() {
            return headers;
        }
    };

    Volley.newRequestQueue(this).add(request);
}

private void stopPrintJob() {
    JSONObject jsonObject = new JSONObject();
    try {
        jsonObject.put("command", "cancel");
    } catch (JSONException e) {
        e.printStackTrace();
        return;
    }

    JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, jobUrl, jsonObject,
            response -> Toast.makeText(MainActivity.this, "Print cancelled", Toast.LENGTH_SHORT).show(), error -> Toast.makeText(MainActivity.this, "Failed to cancel print", Toast.LENGTH_SHORT).show()) {
        @Override
        public Map<String, String> getHeaders() {
            return headers;
        }
    };

    Volley.newRequestQueue(this).add(request);
}

private void restartPrintJob() {
    JSONObject jsonObject = new JSONObject();
    try {
        jsonObject.put("command", "restart");
    } catch (JSONException e) {
        e.printStackTrace();
        return;
    }

    JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, jobUrl, jsonObject,
            response -> Toast.makeText(MainActivity.this, "Print restarted", Toast.LENGTH_SHORT).show(), error -> Toast.makeText(MainActivity.this, "Failed to restart print", Toast.LENGTH_SHORT).show()) {
        @Override
        public Map<String, String> getHeaders() {
            return headers;
        }
    };

    Volley.newRequestQueue(this).add(request);
}

private void handleVolleyError(VolleyError error) {
    String message;
    if (error instanceof TimeoutError || error instanceof NoConnectionError) {
        message = "Timeout or no connection error occurred";
    } else if (error instanceof AuthFailureError) {
        message = "Authentication error occurred";
    } else if (error instanceof ServerError) {
        message = "Server error occurred";
    } else if (error instanceof NetworkError) {
        message = "Network error occurred";
    } else if (error instanceof ParseError) {
        message = "JSON parse error occurred";
    } else {
        message = "Unknown error occurred";
    }
    Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}

}

i'm recieving an "Timeout or no connection error occurred" at the app's launch.

Not a java expert here but seems to do what is needed. Focusing on call to pause, you use the jobUrl.. but that string does not have a lead in http:// it just starts with the OctoPrint_IP. Hard to tell what is included in that. Also I don't see any PORT setup. If your OP instance is on port 80, you are good there but if its not, than that is an issue too.

I would also suggest the use of something like PostMan to do some testing to make sure things work as expected when you run into issues. At least you know the OP Instance will respond to a request as expected. Then you can focus on what is wrong with your app code.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.