From 64bfd7b6caeb2de304fb6205502fad8048337991 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 29 Mar 2025 10:51:00 +0100 Subject: [PATCH] [bambulab] Support `REFRESH` command for all channels (#18457) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * allow refresh Signed-off-by: Martin Grześlowski --- ...dlerFactory.java => AmsDeviceHandler.java} | 23 +- .../internal/BambuLabBindingConstants.java | 13 +- .../internal/BambuLabHandlerFactory.java | 2 +- .../binding/bambulab/internal/Camera.java | 2 +- .../bambulab/internal/PrinterHandler.java | 362 ++++++++---------- .../bambulab/internal/StateParserHelper.java | 70 +++- .../binding/bambulab/internal/TrayHelper.java | 12 +- .../resources/OH-INF/i18n/bambulab.properties | 3 + .../resources/OH-INF/thing/channel-types.xml | 6 + .../resources/OH-INF/thing/thing-types.xml | 4 + .../bambulab/internal/PrinterHandlerTest.java | 12 +- .../internal/StateParserHelperTest.java | 4 +- .../bambulab/internal/TrayHelperTest.java | 21 +- 13 files changed, 295 insertions(+), 239 deletions(-) rename bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/{AmsDeviceHandlerFactory.java => AmsDeviceHandler.java} (93%) diff --git a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/AmsDeviceHandlerFactory.java b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/AmsDeviceHandler.java similarity index 93% rename from bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/AmsDeviceHandlerFactory.java rename to bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/AmsDeviceHandler.java index 4b1ba35ced..6805c6e8bf 100644 --- a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/AmsDeviceHandlerFactory.java +++ b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/AmsDeviceHandler.java @@ -30,6 +30,7 @@ import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,11 +39,12 @@ import org.slf4j.LoggerFactory; * @author Martin Grześlowski - Initial contribution */ @NonNullByDefault -public class AmsDeviceHandlerFactory extends BaseThingHandler { - private Logger logger = LoggerFactory.getLogger(AmsDeviceHandlerFactory.class); +public class AmsDeviceHandler extends BaseThingHandler { + private Logger logger = LoggerFactory.getLogger(AmsDeviceHandler.class); private @Nullable AmsDeviceConfiguration config; + private @Nullable PrinterHandler printer; - public AmsDeviceHandlerFactory(Thing thing) { + public AmsDeviceHandler(Thing thing) { super(thing); } @@ -57,11 +59,11 @@ public class AmsDeviceHandlerFactory extends BaseThingHandler { } private void internalInitialize() throws InitializationException { - var printer = validateBridge(); + printer = validateBridge(); var config = this.config = getConfigAs(AmsDeviceConfiguration.class); config.validateNumber(); - logger = LoggerFactory.getLogger("%s.%s.%d".formatted(AmsDeviceHandlerFactory.class.getName(), - printer.getSerialNumber(), config.number)); + logger = LoggerFactory.getLogger( + "%s.%s.%d".formatted(AmsDeviceHandler.class.getName(), printer.getSerialNumber(), config.number)); updateStatus(ONLINE); } @@ -80,7 +82,11 @@ public class AmsDeviceHandlerFactory extends BaseThingHandler { @Override public void handleCommand(ChannelUID channelUID, Command command) { - // no commands to handle + if (command == RefreshType.REFRESH) { + Optional.ofNullable(printer)// + .flatMap(p -> p.findLatestAms(getAmsNumber()))// + .ifPresent(this::updateAms); + } } public void updateAms(Map ams) { @@ -222,7 +228,8 @@ public class AmsDeviceHandlerFactory extends BaseThingHandler { @Override public void dispose() { + printer = null; config = null; - logger = LoggerFactory.getLogger(AmsDeviceHandlerFactory.class); + logger = LoggerFactory.getLogger(AmsDeviceHandler.class); } } diff --git a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/BambuLabBindingConstants.java b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/BambuLabBindingConstants.java index 3c1e0b7bd1..5d6fe3c997 100644 --- a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/BambuLabBindingConstants.java +++ b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/BambuLabBindingConstants.java @@ -41,7 +41,7 @@ public class BambuLabBindingConstants { public static final ThingTypeUID AMS_THING_TYPE = new ThingTypeUID(BINDING_ID, "ams-device"); @SuppressWarnings("StaticMethodOnlyUsedInOneClass") - public enum Channel { + public enum PrinterChannel { CHANNEL_COMMAND("command", true), CHANNEL_NOZZLE_TEMPERATURE("nozzle-temperature"), CHANNEL_NOZZLE_TARGET_TEMPERATURE("nozzle-target-temperature"), @@ -51,6 +51,7 @@ public class BambuLabBindingConstants { CHANNEL_MC_PRINT_STAGE("mc-print-stage"), CHANNEL_MC_PERCENT("mc-percent"), CHANNEL_MC_REMAINING_TIME("mc-remaining-time"), + CHANNEL_END_DATE("end-date"), CHANNEL_WIFI_SIGNAL("wifi-signal"), CHANNEL_BED_TYPE("bed-type"), CHANNEL_GCODE_FILE("gcode-file", true), @@ -96,12 +97,12 @@ public class BambuLabBindingConstants { private final String name; private final boolean supportCommand; - Channel(String name, boolean supportCommand) { + PrinterChannel(String name, boolean supportCommand) { this.name = name; this.supportCommand = supportCommand; } - private Channel(String name) { + private PrinterChannel(String name) { this(name, false); } @@ -121,6 +122,12 @@ public class BambuLabBindingConstants { public boolean is(ChannelUID channelUID) { return name.equals(channelUID.getId()); } + + public static Optional findChannel(ChannelUID channel) { + return stream(values())// + .filter(c -> c.is(channel))// + .findAny(); + } } public static class AmsChannel { diff --git a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/BambuLabHandlerFactory.java b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/BambuLabHandlerFactory.java index 9b883454a4..469668bb9a 100644 --- a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/BambuLabHandlerFactory.java +++ b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/BambuLabHandlerFactory.java @@ -52,7 +52,7 @@ public class BambuLabHandlerFactory extends BaseThingHandlerFactory { } if (AMS_THING_TYPE.equals(thingTypeUID)) { - return new AmsDeviceHandlerFactory(thing); + return new AmsDeviceHandler(thing); } return null; diff --git a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/Camera.java b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/Camera.java index c8d9441793..b4b9408006 100644 --- a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/Camera.java +++ b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/Camera.java @@ -14,8 +14,8 @@ package org.openhab.binding.bambulab.internal; import static java.lang.Thread.interrupted; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.Channel.*; import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.NO_CAMERA_CERT; +import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.PrinterChannel.*; import static org.openhab.binding.bambulab.internal.PrinterConfiguration.Series.X; import static org.openhab.core.library.types.OnOffType.*; import static org.openhab.core.thing.ThingStatus.*; diff --git a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/PrinterHandler.java b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/PrinterHandler.java index ebea8281dd..b618ccaa5f 100644 --- a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/PrinterHandler.java +++ b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/PrinterHandler.java @@ -19,10 +19,8 @@ import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.function.Function.identity; import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.AmsChannel.*; -import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.Channel.*; +import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.PrinterChannel.*; import static org.openhab.binding.bambulab.internal.StateParserHelper.*; -import static org.openhab.binding.bambulab.internal.TrayHelper.updateTrayLoaded; -import static org.openhab.core.library.unit.SIUnits.CELSIUS; import static org.openhab.core.thing.ThingStatus.OFFLINE; import static org.openhab.core.thing.ThingStatus.ONLINE; import static org.openhab.core.thing.ThingStatus.UNKNOWN; @@ -34,11 +32,10 @@ import static pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PushingCommand import static pl.grzeslowski.jbambuapi.mqtt.PrinterClientConfig.requiredFields; import java.net.URI; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; @@ -48,9 +45,8 @@ import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.library.types.DecimalType; +import org.openhab.binding.bambulab.internal.BambuLabBindingConstants.PrinterChannel; import org.openhab.core.library.types.OnOffType; -import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -60,6 +56,7 @@ import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,7 +87,7 @@ public class PrinterHandler extends BaseBridgeHandler private final AtomicReference<@Nullable ScheduledFuture> reconnectSchedule = new AtomicReference<>(); private final PrinterWatcher printerWatcher = new PrinterWatcher(); private @Nullable PrinterClientConfig config; - private final Collection amses = synchronizedList(new ArrayList<>()); + private final Collection amses = synchronizedList(new ArrayList<>()); private final AtomicReference<@Nullable Report> latestPrinterState = new AtomicReference<>(); public PrinterHandler(Bridge bridge) { @@ -99,7 +96,12 @@ public class PrinterHandler extends BaseBridgeHandler @Override public void handleCommand(ChannelUID channelUID, Command command) { - if (CHANNEL_LED_CHAMBER_LIGHT.is(channelUID) || CHANNEL_LED_WORK_LIGHT.is(channelUID)) { + if (command instanceof RefreshType) { + Optional.of(latestPrinterState)// + .map(AtomicReference::get)// + .map(Report::print)// + .ifPresent(printer -> updateState(channelUID, printer)); + } else if (CHANNEL_LED_CHAMBER_LIGHT.is(channelUID) || CHANNEL_LED_WORK_LIGHT.is(channelUID)) { var ledNode = CHANNEL_LED_CHAMBER_LIGHT.is(channelUID) ? CHAMBER_LIGHT : WORK_LIGHT; var bambuCommand = "ON".equals(command.toFullString()) ? on(ledNode) : off(ledNode); sendCommand(bambuCommand); @@ -130,6 +132,16 @@ public class PrinterHandler extends BaseBridgeHandler } } + private void updateState(ChannelUID channelUid, Report.Print print) { + var someChannel = findChannel(channelUid); + if (someChannel.isEmpty()) { + logger.warn("Cannot find channel for {}", channelUid); + return; + } + var channel = someChannel.get(); + updateState(channel, print); + } + @Override public void initialize() { try { @@ -305,132 +317,143 @@ public class PrinterHandler extends BaseBridgeHandler if (delta == null) { return; } - updatePrinterChannels(delta); + var print = delta.print(); + if (print == null) { + return; + } + stream(PrinterChannel.values()).forEach(channel -> updateState(channel, print)); + // if got new Printer state (and not failed) then make sure that thing status in ONLINE updateStatus(ONLINE); } - private void updatePrinterChannels(Report state) { - // Print - var print = state.print(); - if (print == null) { - return; - } - // tempers - updateCelsiusState(CHANNEL_NOZZLE_TEMPERATURE, print.nozzleTemper()); - updateCelsiusState(CHANNEL_NOZZLE_TARGET_TEMPERATURE, print.nozzleTargetTemper()); - updateCelsiusState(CHANNEL_BED_TEMPERATURE, print.bedTemper()); - updateCelsiusState(CHANNEL_BED_TARGET_TEMPERATURE, print.bedTargetTemper()); - updateCelsiusState(CHANNEL_CHAMBER_TEMPERATURE, print.chamberTemper()); - // string - updateStringState(CHANNEL_MC_PRINT_STAGE, print.mcPrintStage()); - updateStringState(CHANNEL_BED_TYPE, print.bedType()); - updateStringState(CHANNEL_GCODE_FILE, print.gcodeFile()); - updateStringState(CHANNEL_GCODE_STATE, print.gcodeState()); - updateStringState(CHANNEL_REASON, print.reason()); - updateStringState(CHANNEL_RESULT, print.result()); - // percent - updatePercentState(CHANNEL_MC_PERCENT, print.mcPercent()); - updatePercentState(CHANNEL_GCODE_FILE_PREPARE_PERCENT, print.gcodeFilePreparePercent()); - // decimal - parseTimeMinutes(print.mcRemainingTime())// - .ifPresent(time -> updateState(CHANNEL_MC_REMAINING_TIME, time)); - updateDecimalState(CHANNEL_BIG_FAN_1_SPEED, print.bigFan1Speed()); - updateDecimalState(CHANNEL_BIG_FAN_2_SPEED, print.bigFan2Speed()); - updateDecimalState(CHANNEL_HEAT_BREAK_FAN_SPEED, print.heatbreakFanSpeed()); - updateDecimalState(CHANNEL_LAYER_NUM, print.layerNum()); - if (print.spdLvl() != null) { - var speedLevel = PrintSpeedCommand.findByLevel(print.spdLvl()); - updateState(CHANNEL_SPEED_LEVEL, new StringType(speedLevel.toString())); - } - // boolean - updateBooleanState(CHANNEL_TIME_LAPS, print.timelapse()); - updateBooleanState(CHANNEL_USE_AMS, print.useAms()); - updateBooleanState(CHANNEL_VIBRATION_CALIBRATION, print.vibrationCali()); - // lights - updateLightState("chamber_light", CHANNEL_LED_CHAMBER_LIGHT, print.lightsReport()); - updateLightState("work_light", CHANNEL_LED_WORK_LIGHT, print.lightsReport()); - // other - if (print.wifiSignal() != null) { - updateState(CHANNEL_WIFI_SIGNAL, parseWifiChannel(print.wifiSignal())); - } - // ams - Optional.of(print)// - .map(Report.Print::ams)// - .ifPresent(ams -> { - updateState(CHANNEL_AMS_TRAY_NOW, updateTrayLoaded(ams.trayNow())); - updateState(CHANNEL_AMS_TRAY_PREVIOUS, updateTrayLoaded(ams.trayPre())); - }); - Optional.of(print)// - .map(Report.Print::ams)// - .map(Report.Print.Ams::ams)// - .stream()// - .flatMap(Collection::stream)// - .forEach(this::updateAms); - // vtray - Optional.of(print)// - .map(Report.Print::vtTray)// - .ifPresent(this::updateVtray); - } - - private void updateVtray(Report.Print.VtTray vtTray) { - Optional.ofNullable(vtTray.trayType())// - .map(Object::toString)// - .flatMap(TrayType::findTrayType)// - .map(Enum::name)// - .flatMap(StateParserHelper::parseStringType)// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_TRAY_TYPE, trayType)); - Optional.ofNullable(vtTray.trayColor())// - .map(Object::toString)// - .map(StateParserHelper::parseColor)// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_TRAY_COLOR, trayType)); - parseTemperatureType(vtTray.nozzleTempMax())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_NOZZLE_TEMPERATURE_MAX, trayType)); - parseTemperatureType(vtTray.nozzleTempMin())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_NOZZLE_TEMPERATURE_MIN, trayType)); - Optional.ofNullable(vtTray.remain())// - .flatMap(StateParserHelper::parsePercentType)// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_REMAIN, trayType)); - parseDecimalType(vtTray.k())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_K, trayType)); - parseDecimalType(vtTray.n())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_N, trayType)); - parseStringType(vtTray.tagUid())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_TAG_UUID, trayType)); - parseStringType(vtTray.trayIdName())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_TRAY_ID_NAME, trayType)); - parseStringType(vtTray.trayInfoIdx())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_TRAY_INFO_IDX, trayType)); - parseStringType(vtTray.traySubBrands()) - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_TRAY_SUB_BRANDS, trayType)); - parseDecimalType(vtTray.trayWeight())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_TRAY_WEIGHT, trayType)); - parseDecimalType(vtTray.trayDiameter())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_TRAY_DIAMETER, trayType)); - parseTemperatureType(vtTray.trayTemp())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_TRAY_TEMPERATURE, trayType)); - parseDecimalType(vtTray.trayTime())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_TRAY_TIME, trayType)); - parseStringType(vtTray.bedTempType())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_BED_TEMPERATURE_TYPE, trayType)); - parseTemperatureType(vtTray.bedTemp())// - .or(StateParserHelper::undef)// - .ifPresent(trayType -> updateState(CHANNEL_VTRAY_BED_TEMPERATURE, trayType)); + private void updateState(PrinterChannel channel, Report.Print print) { + var vtray = Optional.of(print).map(Report.Print::vtTray); + Optional state = switch (channel) { + // temper + case CHANNEL_NOZZLE_TEMPERATURE -> parseTemperatureType(print.nozzleTemper()); + case CHANNEL_NOZZLE_TARGET_TEMPERATURE -> parseTemperatureType(print.nozzleTargetTemper()); + case CHANNEL_BED_TEMPERATURE -> parseTemperatureType(print.bedTemper()); + case CHANNEL_BED_TARGET_TEMPERATURE -> parseTemperatureType(print.bedTargetTemper()); + case CHANNEL_CHAMBER_TEMPERATURE -> parseTemperatureType(print.chamberTemper()); + // string + case CHANNEL_MC_PRINT_STAGE -> parseStringType(print.mcPrintStage()); + case CHANNEL_BED_TYPE -> parseStringType(print.bedType()); + case CHANNEL_GCODE_FILE -> parseStringType(print.gcodeFile()); + case CHANNEL_GCODE_STATE -> parseStringType(print.gcodeState()); + case CHANNEL_REASON -> parseStringType(print.reason()); + case CHANNEL_RESULT -> parseStringType(print.result()); + // percents + case CHANNEL_MC_PERCENT -> parsePercentType(print.mcPercent()); + case CHANNEL_GCODE_FILE_PREPARE_PERCENT -> parsePercentType(print.gcodeFilePreparePercent()); + // time + case CHANNEL_MC_REMAINING_TIME -> parseTimeMinutes(print.mcRemainingTime()); + // wifi + case CHANNEL_WIFI_SIGNAL -> parseWifiChannel(print.wifiSignal()); + // decimal + case CHANNEL_BIG_FAN_1_SPEED -> parseDecimalType(print.bigFan1Speed()); + case CHANNEL_BIG_FAN_2_SPEED -> parseDecimalType(print.bigFan2Speed()); + case CHANNEL_HEAT_BREAK_FAN_SPEED -> parseDecimalType(print.heatbreakFanSpeed()); + case CHANNEL_LAYER_NUM -> parseDecimalType(print.layerNum()); + // boolean + case CHANNEL_TIME_LAPS -> parseOnOffType(print.timelapse()); + case CHANNEL_USE_AMS -> parseOnOffType(print.useAms()); + case CHANNEL_VIBRATION_CALIBRATION -> parseOnOffType(print.vibrationCali()); + // lights + case CHANNEL_LED_CHAMBER_LIGHT -> parseChamberLightType(print.lightsReport()); + case CHANNEL_LED_WORK_LIGHT -> parseWorkLightType(print.lightsReport()); + // vtray + case CHANNEL_VTRAY_TRAY_TYPE -> // + vtray.map(Report.Print.VtTray::trayType)// + .flatMap(StateParserHelper::parseTrayType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_TRAY_COLOR -> // + vtray.map(Report.Print.VtTray::trayColor)// + .map(StateParserHelper::parseColor)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_NOZZLE_TEMPERATURE_MAX -> // + vtray.map(Report.Print.VtTray::nozzleTempMax)// + .flatMap(StateParserHelper::parseTemperatureType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_NOZZLE_TEMPERATURE_MIN -> // + vtray.map(Report.Print.VtTray::nozzleTempMin)// + .flatMap(StateParserHelper::parseTemperatureType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_REMAIN -> // + vtray.map(Report.Print.VtTray::remain)// + .flatMap(StateParserHelper::parsePercentType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_K -> // + vtray.map(Report.Print.VtTray::k)// + .flatMap(StateParserHelper::parseDecimalType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_N -> // + vtray.map(Report.Print.VtTray::n)// + .flatMap(StateParserHelper::parseDecimalType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_TAG_UUID -> // + vtray.map(Report.Print.VtTray::trayUuid)// + .flatMap(StateParserHelper::parseStringType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_TRAY_ID_NAME -> // + vtray.map(Report.Print.VtTray::trayIdName)// + .flatMap(StateParserHelper::parseStringType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_TRAY_INFO_IDX -> // + vtray.map(Report.Print.VtTray::trayInfoIdx)// + .flatMap(StateParserHelper::parseStringType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_TRAY_SUB_BRANDS -> // + vtray.map(Report.Print.VtTray::traySubBrands)// + .flatMap(StateParserHelper::parseStringType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_TRAY_WEIGHT -> // + vtray.map(Report.Print.VtTray::trayWeight)// + .flatMap(StateParserHelper::parseDecimalType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_TRAY_DIAMETER -> // + vtray.map(Report.Print.VtTray::trayDiameter)// + .flatMap(StateParserHelper::parseDecimalType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_TRAY_TEMPERATURE -> // + vtray.map(Report.Print.VtTray::trayTemp)// + .flatMap(StateParserHelper::parseTemperatureType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_TRAY_TIME -> // + vtray.map(Report.Print.VtTray::trayTime)// + .flatMap(StateParserHelper::parseDecimalType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_BED_TEMPERATURE_TYPE -> // + vtray.map(Report.Print.VtTray::bedTempType)// + .flatMap(StateParserHelper::parseStringType)// + .or(StateParserHelper::undef); + case CHANNEL_VTRAY_BED_TEMPERATURE -> // + vtray.map(Report.Print.VtTray::bedTemp)// + .flatMap(StateParserHelper::parseTemperatureType)// + .or(StateParserHelper::undef); + // ams + case CHANNEL_AMS_TRAY_NOW -> // + Optional.of(print)// + .map(Report.Print::ams)// + .map(Report.Print.Ams::trayNow)// + .flatMap(TrayHelper::findStateForTrayLoaded); + case CHANNEL_AMS_TRAY_PREVIOUS -> // + Optional.of(print)// + .map(Report.Print::ams)// + .map(Report.Print.Ams::trayPre)// + .flatMap(TrayHelper::findStateForTrayLoaded); + // misc + case CHANNEL_SPEED_LEVEL -> parseSpeedLevel(print.spdLvl()); + case CHANNEL_END_DATE -> // + Optional.ofNullable(print.mcRemainingTime())// + .map(time -> ZonedDateTime.now().plusMinutes(time))// + .map(StateParserHelper::parseDateTimeType); + // surrogate channels + case CHANNEL_COMMAND -> Optional.empty(); + case CHANNEL_CAMERA_IMAGE -> Optional.empty(); + case CHANNEL_CAMERA_RECORD -> Optional.empty(); + }; + state.ifPresent(s -> updateState(channel, s)); } private void updateAms(Map amsMap) { @@ -448,68 +471,6 @@ public class PrinterHandler extends BaseBridgeHandler .forEach(ams -> ams.updateAms(amsMap)); } - private void updateCelsiusState(BambuLabBindingConstants.Channel channelId, @Nullable Double temperature) { - if (temperature == null) { - return; - } - updateState(channelId, new QuantityType<>(temperature, CELSIUS)); - } - - private void updateStringState(BambuLabBindingConstants.Channel channelId, @Nullable String string) { - if (string == null) { - return; - } - updateState(channelId, new StringType(string)); - } - - private void updateDecimalState(BambuLabBindingConstants.Channel channelId, @Nullable Number number) { - if (number == null) { - return; - } - updateState(channelId, new DecimalType(number)); - } - - private void updateDecimalState(BambuLabBindingConstants.Channel channelId, @Nullable String number) { - if (number == null) { - return; - } - try { - var state = new DecimalType(Double.parseDouble(number)); - updateState(channelId, state); - } catch (NumberFormatException e) { - logger.debug("Cannot parse decimal number {}", number, e); - updateState(channelId, UNDEF); - } - } - - private void updateBooleanState(BambuLabBindingConstants.Channel channelId, @Nullable Boolean bool) { - if (bool == null) { - return; - } - updateState(channelId, OnOffType.from(bool)); - } - - private void updatePercentState(BambuLabBindingConstants.Channel channelId, @Nullable Integer integer) { - parsePercentType(integer).ifPresent(state -> updateState(channelId, state)); - } - - private void updatePercentState(BambuLabBindingConstants.Channel channelId, @Nullable String integer) { - parsePercentType(integer).ifPresent(state -> updateState(channelId, state)); - } - - private void updateLightState(String lightName, BambuLabBindingConstants.Channel channel, - @Nullable List> lights) { - Optional.ofNullable(lights)// - .stream()// - .flatMap(Collection::stream)// - .filter(map -> lightName.equalsIgnoreCase(map.get("node")))// - .map(map -> map.get("mode"))// - .filter(Objects::nonNull)// - .map(OnOffType::from)// - .findAny()// - .ifPresent(command -> updateState(channel, command)); - } - public void sendCommand(String command) { sendCommand(CommandParser.parseCommand(command)); } @@ -528,7 +489,7 @@ public class PrinterHandler extends BaseBridgeHandler } } - private void updateState(BambuLabBindingConstants.Channel channelID, State state) { + private void updateState(PrinterChannel channelID, State state) { updateState(channelID.getName(), state); } @@ -559,11 +520,15 @@ public class PrinterHandler extends BaseBridgeHandler @Override public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) { - if (!(childHandler instanceof AmsDeviceHandlerFactory ams)) { + if (!(childHandler instanceof AmsDeviceHandler ams)) { return; } amses.add(ams); - Optional.of(latestPrinterState)// + findLatestAms(ams.getAmsNumber()).ifPresent(ams::updateAms); + } + + public Optional> findLatestAms(int amsNumber) { + return Optional.of(latestPrinterState)// .map(AtomicReference::get)// .map(Report::print).map(Report.Print::ams)// .map(Report.Print.Ams::ams)// @@ -572,13 +537,12 @@ public class PrinterHandler extends BaseBridgeHandler .filter(map -> map.containsKey("id"))// .filter(map -> map.get("id") != null)// // in code, we are using 1-4 ordering; in API 0-3 ordering is used - .filter(map -> parseInt(map.get("id").toString()) + 1 == ams.getAmsNumber())// - .forEach(ams::updateAms); + .filter(map -> parseInt(map.get("id").toString()) + 1 == amsNumber).findAny(); } @Override public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { - if (!(childHandler instanceof AmsDeviceHandlerFactory ams)) { + if (!(childHandler instanceof AmsDeviceHandler ams)) { return; } var removed = amses.remove(ams); diff --git a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/StateParserHelper.java b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/StateParserHelper.java index 515c6717a2..6dd45cf4cd 100644 --- a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/StateParserHelper.java +++ b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/StateParserHelper.java @@ -18,12 +18,19 @@ import static org.openhab.core.library.unit.Units.*; import static org.openhab.core.types.UnDefType.UNDEF; import static tech.units.indriya.unit.Units.PERCENT; +import java.time.ZonedDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; import org.openhab.core.types.State; @@ -31,6 +38,8 @@ import org.openhab.core.util.ColorUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PrintSpeedCommand; + /** * @author Martin Grześlowski - Initial contribution */ @@ -44,9 +53,15 @@ public class StateParserHelper { .map(time -> new QuantityType<>(time, MINUTE)); } + public static Optional parseTemperatureType(@Nullable Number value) { + return Optional.ofNullable(value).map(d -> new QuantityType<>(d, CELSIUS)); + } + public static Optional parseTemperatureType(@Nullable String value) { try { - return Optional.ofNullable(value).map(Double::parseDouble).map(d -> new QuantityType<>(d, CELSIUS)); + return Optional.ofNullable(value)// + .map(Double::parseDouble)// + .flatMap(StateParserHelper::parseTemperatureType); } catch (NumberFormatException ex) { logger.debug("Cannot parse: {}", value, ex); return Optional.of(UNDEF); @@ -85,20 +100,23 @@ public class StateParserHelper { return Optional.ofNullable(string).map(StringType::new); } - public static State parseWifiChannel(String wifi) { + public static Optional parseWifiChannel(@Nullable String wifi) { + if (wifi == null) { + return Optional.empty(); + } var matcher = DBM_PATTERN.matcher(wifi); if (!matcher.matches()) { logger.debug("Cannot match {} to {}", wifi, DBM_PATTERN); - return UNDEF; + return Optional.of(UNDEF); } var integer = matcher.group(1); try { var value = parseInt(integer); - return new QuantityType<>(value, DECIBEL_MILLIWATTS); + return Optional.of(new QuantityType<>(value, DECIBEL_MILLIWATTS)); } catch (NumberFormatException e) { logger.debug("Cannot parse integer {} from wifi {}", integer, wifi, e); - return UNDEF; + return Optional.of(UNDEF); } } @@ -115,4 +133,46 @@ public class StateParserHelper { public static Optional undef() { return Optional.of(UNDEF); } + + public static Optional parseOnOffType(@Nullable Boolean bool) { + return Optional.ofNullable(bool).map(OnOffType::from); + } + + public static Optional parseSpeedLevel(@Nullable Integer speedLvl) { + return Optional.ofNullable(speedLvl)// + .map(PrintSpeedCommand::findByLevel)// + .map(Object::toString)// + .flatMap(StateParserHelper::parseStringType); + } + + public static Optional parseTrayType(@Nullable String trayType) { + return Optional.ofNullable(trayType).map(Object::toString)// + .flatMap(BambuLabBindingConstants.AmsChannel.TrayType::findTrayType)// + .map(Enum::name)// + .flatMap(StateParserHelper::parseStringType); + } + + public static Optional parseChamberLightType(@Nullable List> lights) { + return parseLightType("chamber_light", lights); + } + + public static Optional parseWorkLightType(@Nullable List> lights) { + return parseLightType("work_light", lights); + } + + private static Optional parseLightType(String lightName, @Nullable List> lights) { + return Optional.ofNullable(lights)// + .stream()// + .flatMap(Collection::stream)// + .filter(map -> lightName.equalsIgnoreCase(map.get("node")))// + .map(map -> map.get("mode"))// + .filter(Objects::nonNull)// + .map(OnOffType::from)// + .map(state -> (State) state)// + .findAny(); + } + + public static State parseDateTimeType(ZonedDateTime dateTime) { + return new DateTimeType(dateTime); + } } diff --git a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/TrayHelper.java b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/TrayHelper.java index 14a871dbfd..64e55f4842 100644 --- a/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/TrayHelper.java +++ b/bundles/org.openhab.binding.bambulab/src/main/java/org/openhab/binding/bambulab/internal/TrayHelper.java @@ -16,6 +16,8 @@ import static java.lang.Integer.parseInt; import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.AmsChannel.*; import static org.openhab.core.types.UnDefType.UNDEF; +import java.util.Optional; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.library.types.StringType; @@ -31,20 +33,20 @@ class TrayHelper { static final int MAX_TRAY_VALUE = MAX_AMS * MAX_AMS_TRAYS - 1; private static final Logger logger = LoggerFactory.getLogger(TrayHelper.class); - static State updateTrayLoaded(@Nullable String tray) { + static Optional findStateForTrayLoaded(@Nullable String tray) { if (tray == null) { - return UNDEF; + return Optional.empty(); } try { var integer = parseInt(tray); - return findStateForTrayLoaded(integer); + return Optional.of(parseTrayLoaded(integer)); } catch (NumberFormatException e) { logger.debug("Cannot parse: {}", tray, e); - return UNDEF; + return Optional.of(UNDEF); } } - private static State findStateForTrayLoaded(int tray) { + private static State parseTrayLoaded(int tray) { if (tray < 0) { return UNDEF; } diff --git a/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/i18n/bambulab.properties b/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/i18n/bambulab.properties index 8d6076ee1c..81794a3768 100644 --- a/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/i18n/bambulab.properties +++ b/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/i18n/bambulab.properties @@ -30,6 +30,8 @@ thing-type.bambulab.printer.channel.chamber-temperature.label = Chamber Temperat thing-type.bambulab.printer.channel.chamber-temperature.description = Current temperature inside the printer chamber. thing-type.bambulab.printer.channel.command.label = Command thing-type.bambulab.printer.channel.command.description = Send command to run. See Actions > sendCommand +thing-type.bambulab.printer.channel.end-date.label = End Time of the Print +thing-type.bambulab.printer.channel.end-date.description = Estimated end date of the print. thing-type.bambulab.printer.channel.gcode-file.label = G-code File thing-type.bambulab.printer.channel.gcode-file.description = Name of the currently loaded G-code file. thing-type.bambulab.printer.channel.gcode-file-prepare-percent.label = G-code Preparation Progress @@ -170,6 +172,7 @@ channel-type.bambulab.temperature-measurement.label = Current Temperature channel-type.bambulab.temperature-setpoint.label = Target Temperature channel-type.bambulab.time-min.label = Time (min) channel-type.bambulab.time-min.state.pattern = %1$tHh:%1$tMm +channel-type.bambulab.timestamp.label = Time Stamp channel-type.bambulab.tray-color.label = Tray Color channel-type.bambulab.tray-loaded.label = Currently Loaded Tray channel-type.bambulab.tray-loaded.state.option.EMPTY = No tray loaded diff --git a/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/thing/channel-types.xml b/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/thing/channel-types.xml index 662258f349..aaffec9f24 100644 --- a/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/thing/channel-types.xml +++ b/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/thing/channel-types.xml @@ -63,6 +63,12 @@ + + DateTime + + time + + Number diff --git a/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/thing/thing-types.xml index d8fb5028e9..ab05e01959 100644 --- a/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.bambulab/src/main/resources/OH-INF/thing/thing-types.xml @@ -47,6 +47,10 @@ Estimated time remaining for the print in seconds. + + + Estimated end date of the print. + Current WiFi signal strength. diff --git a/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/PrinterHandlerTest.java b/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/PrinterHandlerTest.java index ae542cf33b..95d44e7203 100644 --- a/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/PrinterHandlerTest.java +++ b/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/PrinterHandlerTest.java @@ -15,7 +15,7 @@ package org.openhab.binding.bambulab.internal; import static java.util.Arrays.stream; import static java.util.function.Predicate.not; import static org.mockito.Mockito.*; -import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.Channel.*; +import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.PrinterChannel.*; import static pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.LedControlCommand.*; import static pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.LedControlCommand.LedNode.*; @@ -31,7 +31,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import org.openhab.binding.bambulab.internal.BambuLabBindingConstants.Channel; +import org.openhab.binding.bambulab.internal.BambuLabBindingConstants.PrinterChannel; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.StringType; import org.openhab.core.thing.Bridge; @@ -57,7 +57,7 @@ class PrinterHandlerTest { @ParameterizedTest(name = "Should handle {0} command for {1} channel and send {2}") @MethodSource - public void testSendLightCommand(OnOffType command, Channel channel, Command sendCommand) { + public void testSendLightCommand(OnOffType command, PrinterChannel channel, Command sendCommand) { // Given var channelUID = new ChannelUID("bambulab:printer:test:" + channel); @@ -109,7 +109,7 @@ class PrinterHandlerTest { @ParameterizedTest(name = "Command to channel {0} should not invoke `client.sendCommand`") @MethodSource - public void notImplementedCommands(Channel channel) { + public void notImplementedCommands(PrinterChannel channel) { // Given var channelUID = new ChannelUID("bambulab:printer:test:" + channel); @@ -122,8 +122,8 @@ class PrinterHandlerTest { } static Stream notImplementedCommands() { - return stream(Channel.values())// - .filter(not(Channel::isSupportCommand))// + return stream(PrinterChannel.values())// + .filter(not(PrinterChannel::isSupportCommand))// .map(Arguments::of); } } diff --git a/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/StateParserHelperTest.java b/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/StateParserHelperTest.java index 279e34586d..dbf4564543 100644 --- a/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/StateParserHelperTest.java +++ b/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/StateParserHelperTest.java @@ -100,9 +100,11 @@ class StateParserHelperTest { var wifiString = "-75dBm"; // When - var result = StateParserHelper.parseWifiChannel(wifiString); + var someResult = StateParserHelper.parseWifiChannel(wifiString); // Then + assertThat(someResult).isNotEmpty(); + var result = someResult.get(); assertThat(result).isInstanceOf(QuantityType.class); var quantityType = (QuantityType) result; assertThat(quantityType.intValue()).isEqualTo(-75); diff --git a/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/TrayHelperTest.java b/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/TrayHelperTest.java index 8daec36d30..175e4c10ca 100644 --- a/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/TrayHelperTest.java +++ b/bundles/org.openhab.binding.bambulab/src/test/java/org/openhab/binding/bambulab/internal/TrayHelperTest.java @@ -28,6 +28,7 @@ import org.openhab.core.library.types.StringType; /** * @author Martin Grześlowski - Initial contribution */ +@SuppressWarnings("OptionalGetWithoutIsPresent") @NonNullByDefault class TrayHelperTest { @@ -38,7 +39,7 @@ class TrayHelperTest { var tray = "255"; // When - var result = TrayHelper.updateTrayLoaded(tray); + var result = TrayHelper.findStateForTrayLoaded(tray).get(); // Then assertThat(result).isInstanceOf(StringType.class); @@ -52,7 +53,7 @@ class TrayHelperTest { var tray = "254"; // When - var result = TrayHelper.updateTrayLoaded(tray); + var result = TrayHelper.findStateForTrayLoaded(tray).get(); // Then assertThat(result).isInstanceOf(StringType.class); @@ -63,7 +64,7 @@ class TrayHelperTest { @MethodSource public void happyPath(int tray, String expected) { // When - var result = TrayHelper.updateTrayLoaded(tray + ""); + var result = TrayHelper.findStateForTrayLoaded(tray + "").get(); // Then assertThat(result.toString()).isEqualTo(expected); @@ -100,10 +101,10 @@ class TrayHelperTest { String tray = null; // When - var result = TrayHelper.updateTrayLoaded(tray); + var result = TrayHelper.findStateForTrayLoaded(tray); // Then - assertThat(result).isEqualTo(UNDEF); + assertThat(result).isEmpty(); } @Test @@ -113,7 +114,7 @@ class TrayHelperTest { var tray = "not-a-number"; // When - var result = TrayHelper.updateTrayLoaded(tray); + var result = TrayHelper.findStateForTrayLoaded(tray).get(); // Then assertThat(result).isEqualTo(UNDEF); @@ -126,7 +127,7 @@ class TrayHelperTest { var tray = ""; // When - var result = TrayHelper.updateTrayLoaded(tray); + var result = TrayHelper.findStateForTrayLoaded(tray).get(); // Then assertThat(result).isEqualTo(UNDEF); @@ -139,7 +140,7 @@ class TrayHelperTest { var tray = "-5"; // When - var result = TrayHelper.updateTrayLoaded(tray); + var result = TrayHelper.findStateForTrayLoaded(tray).get(); // Then assertThat(result).isEqualTo(UNDEF); @@ -152,7 +153,7 @@ class TrayHelperTest { var tray = (TrayHelper.MAX_TRAY_VALUE + 1) + ""; // When - var result = TrayHelper.updateTrayLoaded(tray); + var result = TrayHelper.findStateForTrayLoaded(tray).get(); // Then assertThat(result).isEqualTo(UNDEF); @@ -165,7 +166,7 @@ class TrayHelperTest { var tray = TrayHelper.MAX_TRAY_VALUE + ""; // When - var result = TrayHelper.updateTrayLoaded(tray); + var result = TrayHelper.findStateForTrayLoaded(tray).get(); // Then assertThat(result.toString()).isEqualTo("AMS_4_4");