Skip to content

Commit 0054e69

Browse files
Attempt to fix Image preview
Signed-off-by: Scott Hanson <scooter_seh@yahoo.com>
1 parent 528115a commit 0054e69

5 files changed

Lines changed: 85 additions & 41 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.threedprinter.internal.dto.prusa;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.eclipse.jdt.annotation.Nullable;
17+
18+
import com.google.gson.annotations.SerializedName;
19+
20+
/**
21+
* DTO for the PrusaLink GET /api/v1/job response.
22+
*
23+
* <p>
24+
* Unlike /api/v1/status, this endpoint includes the file name and thumbnail reference links for the
25+
* currently loaded job.
26+
*
27+
* @author Scott Hanson - Initial contribution
28+
*/
29+
@NonNullByDefault
30+
public class PrusaJobResponse {
31+
32+
@SerializedName("file")
33+
public @Nullable PrusaJobFile file;
34+
35+
public static class PrusaJobFile {
36+
@SerializedName("name")
37+
public String name = "";
38+
39+
@SerializedName("display_name")
40+
public String displayName = "";
41+
42+
@SerializedName("refs")
43+
public @Nullable PrusaJobFileRefs refs;
44+
}
45+
46+
public static class PrusaJobFileRefs {
47+
@SerializedName("thumbnail")
48+
public String thumbnail = "";
49+
}
50+
}

bundles/org.openhab.binding.threedprinter/src/main/java/org/openhab/binding/threedprinter/internal/dto/prusa/PrusaStatusResponse.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,5 @@ public static class PrusaJobData {
6666

6767
@SerializedName("time_printing")
6868
public int timePrinting;
69-
70-
@SerializedName("file")
71-
public @Nullable PrusaFileData file;
72-
73-
public static class PrusaFileData {
74-
@SerializedName("display_name")
75-
public String displayName = "";
76-
77-
@SerializedName("name")
78-
public String name = "";
79-
80-
@SerializedName("path")
81-
public String path = "";
82-
}
8369
}
8470
}

bundles/org.openhab.binding.threedprinter/src/main/java/org/openhab/binding/threedprinter/internal/handler/KlipperHandler.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ protected void refresh() {
146146

147147
if (!stats.filename.isBlank()) {
148148
if (!stats.filename.equals(lastPreviewFilename)) {
149-
lastPreviewFilename = stats.filename;
150149
fetchAndUpdatePreview(baseUrl, cfg.apiKey, stats.filename);
151150
}
152151
} else {
@@ -200,13 +199,18 @@ private void fetchAndUpdatePreview(String baseUrl, String apiKey, String filenam
200199
if (best == null || best.relativePath.isBlank()) {
201200
return;
202201
}
202+
// relative_path is relative to the gcode file's own directory, not the gcodes root
203+
int lastSlash = filename.lastIndexOf('/');
204+
String dir = lastSlash >= 0 ? filename.substring(0, lastSlash + 1) : "";
205+
String fullPath = dir + best.relativePath;
203206
// Encode each path segment individually to preserve the directory separator
204-
String encodedPath = Arrays.stream(best.relativePath.split("/"))
207+
String encodedPath = Arrays.stream(fullPath.split("/"))
205208
.map(s -> URLEncoder.encode(s, StandardCharsets.UTF_8).replace("+", "%20"))
206209
.collect(Collectors.joining("/"));
207210
byte @Nullable [] bytes = httpGetBytes(baseUrl + "/server/files/gcodes/" + encodedPath, apiKey);
208211
if (bytes != null && bytes.length > 0) {
209212
updateState(CHANNEL_JOB_PREVIEW, new RawType(bytes, "image/png"));
213+
lastPreviewFilename = filename;
210214
}
211215
}
212216

bundles/org.openhab.binding.threedprinter/src/main/java/org/openhab/binding/threedprinter/internal/handler/OctoPrintHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,12 @@ protected void refresh() {
160160
String filename = file.name;
161161
if (!filename.isBlank()) {
162162
if (!filename.equals(lastPreviewFilename)) {
163-
lastPreviewFilename = filename;
164163
String encodedName = URLEncoder.encode(filename, StandardCharsets.UTF_8).replace("+", "%20");
165164
byte @Nullable [] bytes = httpGetBytes(
166165
baseUrl + "/plugin/prusaslicerthumbnails/thumbnail/" + encodedName, cfg.apiKey);
167166
if (bytes != null && bytes.length > 0) {
168167
updateState(CHANNEL_JOB_PREVIEW, new RawType(bytes, "image/png"));
168+
lastPreviewFilename = filename;
169169
}
170170
}
171171
} else {

bundles/org.openhab.binding.threedprinter/src/main/java/org/openhab/binding/threedprinter/internal/handler/PrusaLinkHandler.java

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,14 @@
1414

1515
import static org.openhab.binding.threedprinter.internal.ThreedprinterBindingConstants.*;
1616

17-
import java.net.URLEncoder;
18-
import java.nio.charset.StandardCharsets;
19-
import java.util.Arrays;
20-
import java.util.stream.Collectors;
21-
2217
import javax.measure.quantity.Temperature;
2318

2419
import org.eclipse.jdt.annotation.NonNullByDefault;
2520
import org.eclipse.jdt.annotation.Nullable;
2621
import org.eclipse.jetty.client.HttpClient;
2722
import org.openhab.binding.threedprinter.internal.config.PrusaLinkConfiguration;
23+
import org.openhab.binding.threedprinter.internal.dto.prusa.PrusaJobResponse;
24+
import org.openhab.binding.threedprinter.internal.dto.prusa.PrusaJobResponse.PrusaJobFile;
2825
import org.openhab.binding.threedprinter.internal.dto.prusa.PrusaStatusResponse;
2926
import org.openhab.binding.threedprinter.internal.dto.prusa.PrusaStatusResponse.PrusaJobData;
3027
import org.openhab.binding.threedprinter.internal.dto.prusa.PrusaStatusResponse.PrusaPrinterData;
@@ -126,29 +123,36 @@ protected void refresh() {
126123
updateState(CHANNEL_JOB_PROGRESS, new DecimalType(job.progress));
127124
updateState(CHANNEL_TIME_ELAPSED, new DecimalType(job.timePrinting));
128125
updateState(CHANNEL_TIME_REMAINING, new DecimalType(job.timeRemaining));
129-
PrusaJobData.PrusaFileData file = job.file;
130-
if (file != null) {
131-
String name = file.displayName.isBlank() ? file.name : file.displayName;
132-
updateState(CHANNEL_JOB_NAME, new StringType(name));
133-
134-
if (!file.name.isBlank() && !file.name.equals(lastPreviewFilename)) {
135-
lastPreviewFilename = file.name;
136-
// Prefer the path from the API (e.g. "/usb/benchy.gcode"); fall back to "usb/{name}"
137-
String thumbPath = file.path.isBlank() ? "usb/" + file.name
138-
: file.path.startsWith("/") ? file.path.substring(1) : file.path;
139-
String encodedPath = Arrays.stream(thumbPath.split("/"))
140-
.map(s -> URLEncoder.encode(s, StandardCharsets.UTF_8).replace("+", "%20"))
141-
.collect(Collectors.joining("/"));
142-
byte[] bytes = httpGetBytes(baseUrl + "/thumb/l/" + encodedPath, cfg.apiKey);
143-
if (bytes != null && bytes.length > 0) {
144-
updateState(CHANNEL_JOB_PREVIEW, new RawType(bytes, "image/png"));
145-
}
146-
}
147-
}
148126
} else {
149127
updateState(CHANNEL_JOB_PROGRESS, new DecimalType(0));
128+
}
129+
130+
// /api/v1/status does not include file name or thumbnail info; fetch /api/v1/job for that
131+
updateJobFile(baseUrl, cfg.apiKey);
132+
}
133+
134+
private void updateJobFile(String baseUrl, String apiKey) {
135+
String jobJson = httpGet(baseUrl + "/api/v1/job", apiKey);
136+
PrusaJobResponse jobResponse = jobJson != null ? fromJson(jobJson, PrusaJobResponse.class) : null;
137+
PrusaJobFile file = jobResponse != null ? jobResponse.file : null;
138+
139+
if (file == null) {
150140
updateState(CHANNEL_JOB_NAME, new StringType(""));
151141
lastPreviewFilename = "";
142+
return;
143+
}
144+
145+
String name = file.displayName.isBlank() ? file.name : file.displayName;
146+
updateState(CHANNEL_JOB_NAME, new StringType(name));
147+
148+
String thumbnailRef = file.refs != null ? file.refs.thumbnail : "";
149+
if (thumbnailRef.isBlank() || thumbnailRef.equals(lastPreviewFilename)) {
150+
return;
151+
}
152+
byte[] bytes = httpGetBytes(baseUrl + thumbnailRef, apiKey);
153+
if (bytes != null && bytes.length > 0) {
154+
updateState(CHANNEL_JOB_PREVIEW, new RawType(bytes, "image/png"));
155+
lastPreviewFilename = thumbnailRef;
152156
}
153157
}
154158

0 commit comments

Comments
 (0)