mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-31 05:24:24 +02:00
[bambulab] Support AMS (#18448)
* camera Signed-off-by: Martin Grześlowski <martin.grzeslowski@gmail.com>
This commit is contained in:
@@ -65,6 +65,8 @@ Use `us.mqtt.bambulab.com` as the hostname.
|
||||
| `hostname` | N/A | Text | Yes | IP address of the printer or `us.mqtt.bambulab.com` for cloud mode. |
|
||||
| `accessCode` | N/A | Text | Yes | Access code for the printer. The method of obtaining this varies between local and cloud modes. |
|
||||
| `username` | N/A | Text | No | `bblp` for local mode or your Bambu Lab user (starting with `u_`). (Advanced) |
|
||||
| `reconnectTime` | MQTT | Integer | No | Time in seconds to wait before reconnect. (Advanced) |
|
||||
| `reconnectMax` | MQTT | Integer | No | Max times to try reconnections. (Advanced) |
|
||||
| `scheme` | MQTT | Text | No | URI scheme. (Advanced) |
|
||||
| `port` | MQTT | Integer | No | URI port. (Advanced) |
|
||||
| `cameraPort` | Camera | Integer | No | Camera server port. (Advanced) |
|
||||
@@ -72,35 +74,36 @@ Use `us.mqtt.bambulab.com` as the hostname.
|
||||
|
||||
## Channels
|
||||
|
||||
| Channel ID | Type | Description |
|
||||
|------------------------------|---------------------|------------------------------------------------------------------|
|
||||
| `nozzle-temperature` | Temperature Channel | Current temperature of the nozzle. |
|
||||
| `nozzle-target-temperature` | Temperature Channel | Target temperature of the nozzle. |
|
||||
| `bed-temperature` | Temperature Channel | Current temperature of the heated bed. |
|
||||
| `bed-target-temperature` | Temperature Channel | Target temperature of the heated bed. |
|
||||
| `chamber-temperature` | Temperature Channel | Current temperature inside the printer chamber. |
|
||||
| `mc-print-stage` | String Channel | Current stage of the print process. |
|
||||
| `mc-percent` | Percent Channel | Percentage of the print completed. |
|
||||
| `mc-remaining-time` | Number Channel | Estimated time remaining for the print (in seconds). |
|
||||
| `wifi-signal` | WiFi Channel | Current WiFi signal strength. |
|
||||
| `bed-type` | String Channel | Type of the printer's heated bed. |
|
||||
| `gcode-file` | String Channel | Name of the currently loaded G-code file. |
|
||||
| `gcode-state` | String Channel | Current state of the G-code execution. |
|
||||
| `reason` | String Channel | Reason for pausing or stopping the print. |
|
||||
| `result` | String Channel | Final result or status of the print job. |
|
||||
| `gcode-file-prepare-percent` | Percent Channel | Percentage of G-code file preparation completed. |
|
||||
| `big-fan1-speed` | Number Channel | Speed of the first large cooling fan (RPM). |
|
||||
| `big-fan2-speed` | Number Channel | Speed of the second large cooling fan (RPM). |
|
||||
| `heat-break-fan-speed` | Number Channel | Speed of the heat break cooling fan (RPM). |
|
||||
| `layer-num` | Number Channel | Current layer being printed. |
|
||||
| `speed-level` | Number Channel | Current speed setting of the print job. |
|
||||
| `time-laps` | Boolean Channel | Indicates whether time-lapse recording is enabled. |
|
||||
| `use-ams` | Boolean Channel | Indicates whether the Automatic Material System (AMS) is active. |
|
||||
| `vibration-calibration` | Boolean Channel | Indicates whether vibration calibration has been performed. |
|
||||
| `led-chamber` | On/Off Command | Controls the LED lighting inside the printer chamber. |
|
||||
| `led-work` | On/Off Command | Controls the LED lighting for the work area. |
|
||||
| `camera-record` | Boolean RW Command | Turns on/off OpenHAB's capability of recording. |
|
||||
| `camera-image` | Image | Current image from the printer's camera. |
|
||||
| Channel ID | Type | Description |
|
||||
|------------------------------|---------------------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `command` | String RW Channel | Send yor commands to this channel so they will be send to the printer. Check `Actions` > `sendCommand` for detailed documentation. |
|
||||
| `nozzle-temperature` | Temperature Channel | Current temperature of the nozzle. |
|
||||
| `nozzle-target-temperature` | Temperature Channel | Target temperature of the nozzle. |
|
||||
| `bed-temperature` | Temperature Channel | Current temperature of the heated bed. |
|
||||
| `bed-target-temperature` | Temperature Channel | Target temperature of the heated bed. |
|
||||
| `chamber-temperature` | Temperature Channel | Current temperature inside the printer chamber. |
|
||||
| `mc-print-stage` | String Channel | Current stage of the print process. |
|
||||
| `mc-percent` | Percent Channel | Percentage of the print completed. |
|
||||
| `mc-remaining-time` | Number Channel | Estimated time remaining for the print (in seconds). |
|
||||
| `wifi-signal` | WiFi Channel | Current WiFi signal strength. |
|
||||
| `bed-type` | String Channel | Type of the printer's heated bed. |
|
||||
| `gcode-file` | String Channel | Name of the currently loaded G-code file. |
|
||||
| `gcode-state` | String Channel | Current state of the G-code execution. |
|
||||
| `reason` | String Channel | Reason for pausing or stopping the print. |
|
||||
| `result` | String Channel | Final result or status of the print job. |
|
||||
| `gcode-file-prepare-percent` | Percent Channel | Percentage of G-code file preparation completed. |
|
||||
| `big-fan1-speed` | Number Channel | Speed of the first large cooling fan (RPM). |
|
||||
| `big-fan2-speed` | Number Channel | Speed of the second large cooling fan (RPM). |
|
||||
| `heat-break-fan-speed` | Number Channel | Speed of the heat break cooling fan (RPM). |
|
||||
| `layer-num` | Number Channel | Current layer being printed. |
|
||||
| `speed-level` | Number Channel | Current speed setting of the print job. |
|
||||
| `time-laps` | Boolean Channel | Indicates whether time-lapse recording is enabled. |
|
||||
| `use-ams` | Boolean Channel | Indicates whether the Automatic Material System (AMS) is active. |
|
||||
| `vibration-calibration` | Boolean Channel | Indicates whether vibration calibration has been performed. |
|
||||
| `led-chamber` | On/Off Command | Controls the LED lighting inside the printer chamber. |
|
||||
| `led-work` | On/Off Command | Controls the LED lighting for the work area. |
|
||||
| `camera-record` | Boolean RW Command | Turns on/off OpenHAB's capability of recording. |
|
||||
| `camera-image` | Image | Current image from the printer's camera. |
|
||||
|
||||
## Full Example
|
||||
|
||||
@@ -165,7 +168,7 @@ CommandType:Parameter1:Parameter2:...
|
||||
| `ChangeFilament` | `target(int)`,`currentTemperature(int))`,`targetTemperature(int)` | Changes filament using. |
|
||||
| `AmsUserSetting` | `amsId(int)`,`startupReadOption(boolean)`,`trayReadOption(boolean)` | Sets AMS user settings. |
|
||||
| `AmsFilamentSetting` | `amsId(int)`,`trayId(int)`,`trayInfoIdx(string)`,`trayColor(string)`,`nozzleTempMin(int)`,`nozzleTempMax(int)`,`trayType(string)` | Configures filament settings. |
|
||||
| `AmsControl` | ` RESUME` / `RESET` / `PAUSE` | Sends an AMS control command. |
|
||||
| `AmsControl` | `RESUME` / `RESET` / `PAUSE` | Sends an AMS control command. |
|
||||
| `PrintSpeed` | `SILENT` / `STANDARD` / `SPORT` / `LUDICROUS` | Adjusts print speed. |
|
||||
| `GCodeFile` | `filename(string)` | Loads a G-code file. |
|
||||
| `GCodeLine` | `userId(string)\nlines(string...)` | Sends multiple G-code lines. Lines are enter (`\n`) separated |
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>pl.grzeslowski</groupId>
|
||||
<artifactId>JBambuAPI</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
@@ -34,6 +34,7 @@
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.16</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- TEST -->
|
||||
@@ -49,6 +50,12 @@
|
||||
<version>5.11.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.10.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.AmsChannel.*;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.CONFIGURATION_ERROR;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* @author Martin Grześlowski - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AmsDeviceConfiguration {
|
||||
public int number = -1;
|
||||
|
||||
public void validateNumber() throws InitializationException {
|
||||
if (number < MIN_AMS || number > MAX_AMS) {
|
||||
var message = "AMS number has to be between %s and %s!".formatted(MIN_AMS, MAX_AMS);
|
||||
throw new InitializationException(CONFIGURATION_ERROR, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.AmsChannel.MAX_AMS_TRAYS;
|
||||
import static org.openhab.core.thing.ThingStatus.*;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.BRIDGE_UNINITIALIZED;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bambulab.internal.BambuLabBindingConstants.AmsChannel;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
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.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Martin Grześlowski - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AmsDeviceHandlerFactory extends BaseThingHandler {
|
||||
private Logger logger = LoggerFactory.getLogger(AmsDeviceHandlerFactory.class);
|
||||
private @Nullable AmsDeviceConfiguration config;
|
||||
|
||||
public AmsDeviceHandlerFactory(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
try {
|
||||
internalInitialize();
|
||||
} catch (InitializationException e) {
|
||||
logger.debug("Error during initialization", e);
|
||||
updateStatus(OFFLINE, e.getThingStatusDetail(), e.getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
private void internalInitialize() throws InitializationException {
|
||||
var 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));
|
||||
updateStatus(ONLINE);
|
||||
}
|
||||
|
||||
private PrinterHandler validateBridge() throws InitializationException {
|
||||
var bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
throw new InitializationException(BRIDGE_UNINITIALIZED,
|
||||
"@text/thing-type.config.bambulab.ams-device.init.no-bridge");
|
||||
}
|
||||
if (!(bridge.getHandler() instanceof PrinterHandler printer)) {
|
||||
throw new InitializationException(BRIDGE_UNINITIALIZED,
|
||||
"@text/thing-type.config.bambulab.ams-device.init.bridge-wrong-type");
|
||||
}
|
||||
return printer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// no commands to handle
|
||||
}
|
||||
|
||||
public void updateAms(Map<String, Object> ams) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
var number = Optional.ofNullable(config)//
|
||||
.map(c -> c.number)//
|
||||
.map(Objects::toString)//
|
||||
.orElse("?!");
|
||||
logger.debug("Updating AMS #{}", number);
|
||||
}
|
||||
Optional.of(ams)//
|
||||
.map(map -> map.get("tray"))//
|
||||
.filter(obj -> obj instanceof Collection<?>)//
|
||||
.map(obj -> (Collection<?>) obj)//
|
||||
.stream()//
|
||||
.flatMap(Collection::stream)//
|
||||
.filter(obj -> obj instanceof Map<?, ?>)//
|
||||
.map(obj -> (Map<?, ?>) obj)//
|
||||
.forEach(this::updateAmsTray);
|
||||
}
|
||||
|
||||
private void updateAmsTray(Map<?, ?> map) {
|
||||
var someId = findKey(map, "id")//
|
||||
.map(Object::toString)//
|
||||
.map(Integer::parseInt)
|
||||
// tray ID in api starts from 0 and for channels it starts for 1
|
||||
.map(t -> t + 1);
|
||||
if (someId.isEmpty()) {
|
||||
logger.warn("There is no tray ID in {}", map);
|
||||
return;
|
||||
}
|
||||
int trayId = someId.get();
|
||||
if (trayId > MAX_AMS_TRAYS) {
|
||||
logger.warn("Tray ID needs to be lower that {}. Was {}", MAX_AMS_TRAYS, trayId);
|
||||
return;
|
||||
}
|
||||
|
||||
findKey(map, "tray_type")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(AmsChannel.TrayType::findTrayType)//
|
||||
.map(Enum::name)//
|
||||
.map(value -> (State) StringType.valueOf(value))//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getTrayTypeChannel(trayId), value));
|
||||
findKey(map, "tray_color")//
|
||||
.map(Object::toString)//
|
||||
.map(StateParserHelper::parseColor)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getTrayColorChannel(trayId), value));
|
||||
findKey(map, "nozzle_temp_max")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseTemperatureType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getNozzleTemperatureMaxChannel(trayId), value));
|
||||
findKey(map, "nozzle_temp_min")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseTemperatureType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getNozzleTemperatureMinChannel(trayId), value));
|
||||
findKey(map, "remain")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parsePercentType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getRemainChannel(trayId), value));
|
||||
findKey(map, "k")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseDecimalType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getKChannel(trayId), value));
|
||||
findKey(map, "n")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseDecimalType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getNChannel(trayId), value));
|
||||
findKey(map, "tag_uuid")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseStringType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getTagUuidChannel(trayId), value));
|
||||
findKey(map, "tray_id_name")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseStringType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getTrayIdNameChannel(trayId), value));
|
||||
findKey(map, "tray_info_idx")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseStringType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getTrayInfoIdxChannel(trayId), value));
|
||||
findKey(map, "tray_sub_brands")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseStringType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getTraySubBrandsChannel(trayId), value));
|
||||
findKey(map, "tray_weight")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseDecimalType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getTrayWeightChannel(trayId), value));
|
||||
findKey(map, "tray_diameter")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseDecimalType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getTrayDiameterChannel(trayId), value));
|
||||
findKey(map, "tray_temp")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseTemperatureType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getTrayTemperatureChannel(trayId), value));
|
||||
findKey(map, "tray_time")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseDecimalType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getTrayTimeChannel(trayId), value));
|
||||
findKey(map, "bed_temp_type")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseStringType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getBedTemperatureTypeChannel(trayId), value));
|
||||
findKey(map, "bed_temp")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseTemperatureType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getBedTemperatureChannel(trayId), value));
|
||||
findKey(map, "ctype")//
|
||||
.map(Object::toString)//
|
||||
.flatMap(StateParserHelper::parseDecimalType)//
|
||||
.or(StateParserHelper::undef)//
|
||||
.ifPresent(value -> updateState(AmsChannel.getCtypeChannel(trayId), value));
|
||||
}
|
||||
|
||||
private static Optional<?> findKey(Map<?, ?> map, String key) {
|
||||
return Optional.of(map).map(m -> m.get(key));
|
||||
}
|
||||
|
||||
public int getAmsNumber() {
|
||||
return requireNonNull(config, "At this point config should be initialized").number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
config = null;
|
||||
logger = LoggerFactory.getLogger(AmsDeviceHandlerFactory.class);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -59,7 +59,7 @@ public interface BambuHandler {
|
||||
|
||||
/**
|
||||
* Returns current scheduler
|
||||
*
|
||||
*
|
||||
* @return scheduler
|
||||
*/
|
||||
ScheduledExecutorService getScheduler();
|
||||
|
||||
+148
@@ -12,9 +12,15 @@
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link BambuLabBindingConstants} class defines common constants, which are
|
||||
@@ -32,9 +38,11 @@ public class BambuLabBindingConstants {
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID PRINTER_THING_TYPE = new ThingTypeUID(BINDING_ID, "printer");
|
||||
public static final ThingTypeUID AMS_THING_TYPE = new ThingTypeUID(BINDING_ID, "ams-device");
|
||||
|
||||
@SuppressWarnings("StaticMethodOnlyUsedInOneClass")
|
||||
public enum Channel {
|
||||
CHANNEL_COMMAND("command", true),
|
||||
CHANNEL_NOZZLE_TEMPERATURE("nozzle-temperature"),
|
||||
CHANNEL_NOZZLE_TARGET_TEMPERATURE("nozzle-target-temperature"),
|
||||
CHANNEL_BED_TEMPERATURE("bed-temperature"),
|
||||
@@ -60,6 +68,28 @@ public class BambuLabBindingConstants {
|
||||
CHANNEL_VIBRATION_CALIBRATION("vibration-calibration"),
|
||||
CHANNEL_CAMERA_RECORD("camera-record", true),
|
||||
CHANNEL_CAMERA_IMAGE("camera-image"),
|
||||
// vtray
|
||||
CHANNEL_VTRAY_TRAY_TYPE("vtray-tray-type"),
|
||||
CHANNEL_VTRAY_TRAY_COLOR("vtray-tray-color"),
|
||||
CHANNEL_VTRAY_NOZZLE_TEMPERATURE_MAX("vtray-nozzle-temperature-max"),
|
||||
CHANNEL_VTRAY_NOZZLE_TEMPERATURE_MIN("vtray-nozzle-temperature-min"),
|
||||
CHANNEL_VTRAY_REMAIN("vtray-remain"),
|
||||
CHANNEL_VTRAY_K("vtray-k"),
|
||||
CHANNEL_VTRAY_N("vtray-n"),
|
||||
CHANNEL_VTRAY_TAG_UUID("vtray-tag-uuid"),
|
||||
CHANNEL_VTRAY_TRAY_ID_NAME("vtray-tray-id-name"),
|
||||
CHANNEL_VTRAY_TRAY_INFO_IDX("vtray-tray-info-idx"),
|
||||
CHANNEL_VTRAY_TRAY_SUB_BRANDS("vtray-tray-sub-brands"),
|
||||
CHANNEL_VTRAY_TRAY_WEIGHT("vtray-tray-weight"),
|
||||
CHANNEL_VTRAY_TRAY_DIAMETER("vtray-tray-diameter"),
|
||||
CHANNEL_VTRAY_TRAY_TEMPERATURE("vtray-tray-temperature"),
|
||||
CHANNEL_VTRAY_TRAY_TIME("vtray-tray-time"),
|
||||
CHANNEL_VTRAY_BED_TEMPERATURE_TYPE("vtray-bed-temp-type"),
|
||||
CHANNEL_VTRAY_BED_TEMPERATURE("vtray-bed-temperature"),
|
||||
// AMS generic
|
||||
CHANNEL_AMS_TRAY_NOW("ams-tray-now"),
|
||||
CHANNEL_AMS_TRAY_PREVIOUS("ams-tray-previous"),
|
||||
// leds
|
||||
CHANNEL_LED_CHAMBER_LIGHT("led-chamber", true),
|
||||
CHANNEL_LED_WORK_LIGHT("led-work", true);
|
||||
|
||||
@@ -92,4 +122,122 @@ public class BambuLabBindingConstants {
|
||||
return name.equals(channelUID.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public static class AmsChannel {
|
||||
public static final int MIN_AMS = 1;
|
||||
/**
|
||||
* According to Bambu Lab documentation, you can attach up to 4 AMS
|
||||
*/
|
||||
public static final int MAX_AMS = 4;
|
||||
/**
|
||||
* Each AMS device has 4 trays
|
||||
*/
|
||||
public static final int MAX_AMS_TRAYS = 4;
|
||||
|
||||
public static String getTrayTypeChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-tray-type";
|
||||
}
|
||||
|
||||
public static String getTrayColorChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-tray-color";
|
||||
}
|
||||
|
||||
public static String getNozzleTemperatureMaxChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-nozzle-temperature-max";
|
||||
}
|
||||
|
||||
public static String getNozzleTemperatureMinChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-nozzle-temperature-min";
|
||||
}
|
||||
|
||||
public static String getRemainChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-remain";
|
||||
}
|
||||
|
||||
public static String getKChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-k";
|
||||
}
|
||||
|
||||
public static String getNChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-n";
|
||||
}
|
||||
|
||||
public static String getTagUuidChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-tag-uuid";
|
||||
}
|
||||
|
||||
public static String getTrayIdNameChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-tray-id-name";
|
||||
}
|
||||
|
||||
public static String getTrayInfoIdxChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-tray-info-idx";
|
||||
}
|
||||
|
||||
public static String getTraySubBrandsChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-tray-sub-brands";
|
||||
}
|
||||
|
||||
public static String getTrayWeightChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-tray-weight";
|
||||
}
|
||||
|
||||
public static String getTrayDiameterChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-tray-diameter";
|
||||
}
|
||||
|
||||
public static String getTrayTemperatureChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-tray-temperature";
|
||||
}
|
||||
|
||||
public static String getTrayTimeChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-tray-time";
|
||||
}
|
||||
|
||||
public static String getBedTemperatureTypeChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-bed-temp-type";
|
||||
}
|
||||
|
||||
public static String getBedTemperatureChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-bed-temperature";
|
||||
}
|
||||
|
||||
public static String getCtypeChannel(int trayId) {
|
||||
return prefix(trayId) + "ams-ctype";
|
||||
}
|
||||
|
||||
private static String prefix(int trayId) {
|
||||
checkTrayId(trayId);
|
||||
return "ams-tray-%s#".formatted(trayId + 1);
|
||||
}
|
||||
|
||||
private static void checkTrayId(int trayId) {
|
||||
if (trayId <= 0 || trayId > MAX_AMS_TRAYS) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid tray ID: %d. Allowed range: 1 to %d.".formatted(trayId, MAX_AMS_TRAYS));
|
||||
}
|
||||
}
|
||||
|
||||
public static enum TrayType {
|
||||
PLA,
|
||||
PETG,
|
||||
ABS,
|
||||
TPU,
|
||||
ASA,
|
||||
PA,
|
||||
PC,
|
||||
PVA,
|
||||
HIPS;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TrayType.class);
|
||||
|
||||
public static Optional<TrayType> findTrayType(String name) {
|
||||
var any = stream(values()).filter(t -> t.name().equalsIgnoreCase(name)).findAny();
|
||||
if (any.isEmpty()) {
|
||||
log.warn("Cannot parse TrayType from {}!", name);
|
||||
}
|
||||
return any;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -12,12 +12,13 @@
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.PRINTER_THING_TYPE;
|
||||
import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
@@ -35,7 +36,7 @@ import org.osgi.service.component.annotations.Component;
|
||||
@Component(configurationPid = "binding.bambulab", service = ThingHandlerFactory.class)
|
||||
public class BambuLabHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(PRINTER_THING_TYPE);
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(PRINTER_THING_TYPE, AMS_THING_TYPE);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
@@ -46,8 +47,12 @@ public class BambuLabHandlerFactory extends BaseThingHandlerFactory {
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (PRINTER_THING_TYPE.equals(thingTypeUID)) {
|
||||
return new PrinterHandler(thing);
|
||||
if (PRINTER_THING_TYPE.equals(thingTypeUID) && thing instanceof Bridge bridge) {
|
||||
return new PrinterHandler(bridge);
|
||||
}
|
||||
|
||||
if (AMS_THING_TYPE.equals(thingTypeUID)) {
|
||||
return new AmsDeviceHandlerFactory(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static java.lang.Boolean.parseBoolean;
|
||||
import static java.lang.Integer.parseInt;
|
||||
import static java.util.Arrays.copyOfRange;
|
||||
import static pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.LedControlCommand.LedMode.FLASHING;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient;
|
||||
|
||||
/**
|
||||
* @author Martin Grzeslowski - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CommandParser {
|
||||
|
||||
public static PrinterClient.Channel.Command parseCommand(String stringCommand) {
|
||||
var split = stringCommand.split(":");
|
||||
if (split.length <= 1) {
|
||||
throw new IllegalArgumentException("Command too short, class name not passed. Command: " + stringCommand);
|
||||
}
|
||||
var commandName = split[0] + "Command";
|
||||
var tail = tail(split);
|
||||
if (commandName.equals(PrinterClient.Channel.InfoCommand.class.getSimpleName())) {
|
||||
return parseInfoCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.PushingCommand.class.getSimpleName())) {
|
||||
return parsePushingCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.PrintCommand.class.getSimpleName())) {
|
||||
return parsePrintCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.ChangeFilamentCommand.class.getSimpleName())) {
|
||||
return parseChangeFilamentCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.AmsUserSettingCommand.class.getSimpleName())) {
|
||||
return parseAmsUserSettingCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.AmsFilamentSettingCommand.class.getSimpleName())) {
|
||||
return parseAmsFilamentSettingCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.AmsControlCommand.class.getSimpleName())) {
|
||||
return parseAmsControlCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.PrintSpeedCommand.class.getSimpleName())) {
|
||||
return parsePrintSpeedCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.GCodeFileCommand.class.getSimpleName())) {
|
||||
return parseGCodeFileCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.GCodeLineCommand.class.getSimpleName())) {
|
||||
var gcodeLineSplit = stringCommand.split(":", 2);
|
||||
requireLength(gcodeLineSplit, 2);
|
||||
return parseGCodeLineCommand(gcodeLineSplit[1]);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.LedControlCommand.class.getSimpleName())) {
|
||||
return parseLedControlCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.SystemCommand.class.getSimpleName())) {
|
||||
return parseSystemCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.IpCamRecordCommand.class.getSimpleName())) {
|
||||
return parseIpCamRecordCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.IpCamTimelapsCommand.class.getSimpleName())) {
|
||||
return parseIpCamTimelapsCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrinterClient.Channel.XCamControlCommand.class.getSimpleName())) {
|
||||
return parseXCamControlCommand(tail);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown command name: " + commandName);
|
||||
}
|
||||
|
||||
private static String[] tail(String[] command) {
|
||||
return copyOfRange(command, 1, command.length);
|
||||
}
|
||||
|
||||
private static void requireLength(String[] commandLine, int length) {
|
||||
if (commandLine.length != length) {
|
||||
throw new IllegalArgumentException("Command line length does not match! Should be %s, but was %s!"
|
||||
.formatted(length, commandLine.length));
|
||||
}
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.InfoCommand parseInfoCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return PrinterClient.Channel.InfoCommand.valueOf(commandLine[0]);
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.PushingCommand parsePushingCommand(String[] commandLine) {
|
||||
if (commandLine.length == 0) {
|
||||
return PrinterClient.Channel.PushingCommand.defaultPushingCommand();
|
||||
}
|
||||
requireLength(commandLine, 2);
|
||||
return new PrinterClient.Channel.PushingCommand(parseInt(commandLine[0]), parseInt(commandLine[1]));
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.PrintCommand parsePrintCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return PrinterClient.Channel.PrintCommand.valueOf(commandLine[0]);
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.ChangeFilamentCommand parseChangeFilamentCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 3);
|
||||
return new PrinterClient.Channel.ChangeFilamentCommand(parseInt(commandLine[0]), parseInt(commandLine[1]),
|
||||
parseInt(commandLine[2]));
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.AmsUserSettingCommand parseAmsUserSettingCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 3);
|
||||
return new PrinterClient.Channel.AmsUserSettingCommand(parseInt(commandLine[0]), parseBoolean(commandLine[1]),
|
||||
parseBoolean(commandLine[2]));
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.AmsFilamentSettingCommand parseAmsFilamentSettingCommand(
|
||||
String[] commandLine) {
|
||||
requireLength(commandLine, 7);
|
||||
return new PrinterClient.Channel.AmsFilamentSettingCommand(parseInt(commandLine[0]), parseInt(commandLine[1]),
|
||||
commandLine[2], commandLine[3], parseInt(commandLine[4]), parseInt(commandLine[5]), commandLine[6]);
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.AmsControlCommand parseAmsControlCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return PrinterClient.Channel.AmsControlCommand.valueOf(commandLine[0]);
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.PrintSpeedCommand parsePrintSpeedCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return PrinterClient.Channel.PrintSpeedCommand.valueOf(commandLine[0]);
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.GCodeFileCommand parseGCodeFileCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return new PrinterClient.Channel.GCodeFileCommand(commandLine[0]);
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.GCodeLineCommand parseGCodeLineCommand(String commandLine) {
|
||||
var split = commandLine.split("\n");
|
||||
if (split.length < 2) {
|
||||
throw new IllegalArgumentException("There are no lines for GCodeLineCommand!");
|
||||
}
|
||||
var lines = Arrays.stream(split).skip(1).toList();
|
||||
return new PrinterClient.Channel.GCodeLineCommand(lines, split[0]);
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.LedControlCommand parseLedControlCommand(String[] commandLine) {
|
||||
if (commandLine.length < 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"Command line length does not match! Should be %s, but was %s!".formatted(2, commandLine.length));
|
||||
}
|
||||
var ledNode = PrinterClient.Channel.LedControlCommand.LedNode.valueOf(commandLine[0]);
|
||||
var ledMode = PrinterClient.Channel.LedControlCommand.LedMode.valueOf(commandLine[1]);
|
||||
@Nullable
|
||||
Integer ledOnTime = null, ledOffTime = null, loopTimes = null, intervalTime = null;
|
||||
if (ledMode == FLASHING) {
|
||||
requireLength(commandLine, 6);
|
||||
ledOnTime = parseInt(commandLine[2]);
|
||||
ledOffTime = parseInt(commandLine[3]);
|
||||
loopTimes = parseInt(commandLine[4]);
|
||||
intervalTime = parseInt(commandLine[5]);
|
||||
}
|
||||
return new PrinterClient.Channel.LedControlCommand(ledNode, ledMode, ledOnTime, ledOffTime, loopTimes,
|
||||
intervalTime);
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.SystemCommand parseSystemCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return PrinterClient.Channel.SystemCommand.valueOf(commandLine[0]);
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.IpCamRecordCommand parseIpCamRecordCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return new PrinterClient.Channel.IpCamRecordCommand(parseBoolean(commandLine[0]));
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.IpCamTimelapsCommand parseIpCamTimelapsCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return new PrinterClient.Channel.IpCamTimelapsCommand(parseBoolean(commandLine[0]));
|
||||
}
|
||||
|
||||
private static PrinterClient.Channel.XCamControlCommand parseXCamControlCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 3);
|
||||
return new PrinterClient.Channel.XCamControlCommand(
|
||||
PrinterClient.Channel.XCamControlCommand.Module.valueOf(commandLine[0]), parseBoolean(commandLine[1]),
|
||||
parseBoolean(commandLine[2]));
|
||||
}
|
||||
}
|
||||
+1
-195
@@ -12,14 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static java.lang.Boolean.parseBoolean;
|
||||
import static java.lang.Integer.parseInt;
|
||||
import static java.util.Arrays.copyOfRange;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.BINDING_ID;
|
||||
import static pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.LedControlCommand.LedMode.FLASHING;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@@ -29,25 +23,6 @@ import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.AmsControlCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.AmsFilamentSettingCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.AmsUserSettingCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.ChangeFilamentCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.Command;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.GCodeFileCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.GCodeLineCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.InfoCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.IpCamRecordCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.IpCamTimelapsCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.LedControlCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.LedControlCommand.LedMode;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.LedControlCommand.LedNode;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PrintCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PrintSpeedCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PushingCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.SystemCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.XCamControlCommand;
|
||||
|
||||
/**
|
||||
* @author Martin Grzeslowski - Initial contribution
|
||||
*/
|
||||
@@ -74,176 +49,7 @@ public class PrinterActions implements ThingActions {
|
||||
return;
|
||||
}
|
||||
|
||||
var command = parseCommand(stringCommand);
|
||||
localHandler.sendCommand(command);
|
||||
}
|
||||
|
||||
private Command parseCommand(String stringCommand) {
|
||||
var split = stringCommand.split(":");
|
||||
if (split.length <= 1) {
|
||||
throw new IllegalArgumentException("Command too short, class name not passed. Command: " + stringCommand);
|
||||
}
|
||||
var commandName = split[0] + "Command";
|
||||
var tail = tail(split);
|
||||
if (commandName.equals(InfoCommand.class.getSimpleName())) {
|
||||
return parseInfoCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PushingCommand.class.getSimpleName())) {
|
||||
return parsePushingCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrintCommand.class.getSimpleName())) {
|
||||
return parsePrintCommand(tail);
|
||||
}
|
||||
if (commandName.equals(ChangeFilamentCommand.class.getSimpleName())) {
|
||||
return parseChangeFilamentCommand(tail);
|
||||
}
|
||||
if (commandName.equals(AmsUserSettingCommand.class.getSimpleName())) {
|
||||
return parseAmsUserSettingCommand(tail);
|
||||
}
|
||||
if (commandName.equals(AmsFilamentSettingCommand.class.getSimpleName())) {
|
||||
return parseAmsFilamentSettingCommand(tail);
|
||||
}
|
||||
if (commandName.equals(AmsControlCommand.class.getSimpleName())) {
|
||||
return parseAmsControlCommand(tail);
|
||||
}
|
||||
if (commandName.equals(PrintSpeedCommand.class.getSimpleName())) {
|
||||
return parsePrintSpeedCommand(tail);
|
||||
}
|
||||
if (commandName.equals(GCodeFileCommand.class.getSimpleName())) {
|
||||
return parseGCodeFileCommand(tail);
|
||||
}
|
||||
if (commandName.equals(GCodeLineCommand.class.getSimpleName())) {
|
||||
var gcodeLineSplit = stringCommand.split(":", 2);
|
||||
requireLength(gcodeLineSplit, 2);
|
||||
return parseGCodeLineCommand(gcodeLineSplit[1]);
|
||||
}
|
||||
if (commandName.equals(LedControlCommand.class.getSimpleName())) {
|
||||
return parseLedControlCommand(tail);
|
||||
}
|
||||
if (commandName.equals(SystemCommand.class.getSimpleName())) {
|
||||
return parseSystemCommand(tail);
|
||||
}
|
||||
if (commandName.equals(IpCamRecordCommand.class.getSimpleName())) {
|
||||
return parseIpCamRecordCommand(tail);
|
||||
}
|
||||
if (commandName.equals(IpCamTimelapsCommand.class.getSimpleName())) {
|
||||
return parseIpCamTimelapsCommand(tail);
|
||||
}
|
||||
if (commandName.equals(XCamControlCommand.class.getSimpleName())) {
|
||||
return parseXCamControlCommand(tail);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown command name: " + commandName);
|
||||
}
|
||||
|
||||
private String[] tail(String[] command) {
|
||||
return copyOfRange(command, 1, command.length);
|
||||
}
|
||||
|
||||
private void requireLength(String[] commandLine, int length) {
|
||||
if (commandLine.length != length) {
|
||||
throw new IllegalArgumentException("Command line length does not match! Should be %s, but was %s!"
|
||||
.formatted(length, commandLine.length));
|
||||
}
|
||||
}
|
||||
|
||||
private InfoCommand parseInfoCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return InfoCommand.valueOf(commandLine[0]);
|
||||
}
|
||||
|
||||
private PushingCommand parsePushingCommand(String[] commandLine) {
|
||||
if (commandLine.length == 0) {
|
||||
return PushingCommand.defaultPushingCommand();
|
||||
}
|
||||
requireLength(commandLine, 2);
|
||||
return new PushingCommand(parseInt(commandLine[0]), parseInt(commandLine[1]));
|
||||
}
|
||||
|
||||
private PrintCommand parsePrintCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return PrintCommand.valueOf(commandLine[0]);
|
||||
}
|
||||
|
||||
private ChangeFilamentCommand parseChangeFilamentCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 3);
|
||||
return new ChangeFilamentCommand(parseInt(commandLine[0]), parseInt(commandLine[1]), parseInt(commandLine[2]));
|
||||
}
|
||||
|
||||
private AmsUserSettingCommand parseAmsUserSettingCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 3);
|
||||
return new AmsUserSettingCommand(parseInt(commandLine[0]), parseBoolean(commandLine[1]),
|
||||
parseBoolean(commandLine[2]));
|
||||
}
|
||||
|
||||
private AmsFilamentSettingCommand parseAmsFilamentSettingCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 7);
|
||||
return new AmsFilamentSettingCommand(parseInt(commandLine[0]), parseInt(commandLine[1]), commandLine[2],
|
||||
commandLine[3], parseInt(commandLine[4]), parseInt(commandLine[5]), commandLine[6]);
|
||||
}
|
||||
|
||||
private AmsControlCommand parseAmsControlCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return AmsControlCommand.valueOf(commandLine[0]);
|
||||
}
|
||||
|
||||
private PrintSpeedCommand parsePrintSpeedCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return PrintSpeedCommand.valueOf(commandLine[0]);
|
||||
}
|
||||
|
||||
private GCodeFileCommand parseGCodeFileCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return new GCodeFileCommand(commandLine[0]);
|
||||
}
|
||||
|
||||
private GCodeLineCommand parseGCodeLineCommand(String commandLine) {
|
||||
var split = commandLine.split("\n");
|
||||
if (split.length < 2) {
|
||||
throw new IllegalArgumentException("There are no lines for GCodeLineCommand!");
|
||||
}
|
||||
var lines = Arrays.stream(split).skip(1).toList();
|
||||
return new GCodeLineCommand(lines, split[0]);
|
||||
}
|
||||
|
||||
private LedControlCommand parseLedControlCommand(String[] commandLine) {
|
||||
if (commandLine.length < 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"Command line length does not match! Should be %s, but was %s!".formatted(2, commandLine.length));
|
||||
}
|
||||
var ledNode = LedNode.valueOf(commandLine[0]);
|
||||
var ledMode = LedMode.valueOf(commandLine[1]);
|
||||
@Nullable
|
||||
Integer ledOnTime = null, ledOffTime = null, loopTimes = null, intervalTime = null;
|
||||
if (ledMode == FLASHING) {
|
||||
requireLength(commandLine, 6);
|
||||
ledOnTime = parseInt(commandLine[2]);
|
||||
ledOffTime = parseInt(commandLine[3]);
|
||||
loopTimes = parseInt(commandLine[4]);
|
||||
intervalTime = parseInt(commandLine[5]);
|
||||
}
|
||||
return new LedControlCommand(ledNode, ledMode, ledOnTime, ledOffTime, loopTimes, intervalTime);
|
||||
}
|
||||
|
||||
private SystemCommand parseSystemCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return SystemCommand.valueOf(commandLine[0]);
|
||||
}
|
||||
|
||||
private IpCamRecordCommand parseIpCamRecordCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return new IpCamRecordCommand(parseBoolean(commandLine[0]));
|
||||
}
|
||||
|
||||
private IpCamTimelapsCommand parseIpCamTimelapsCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 1);
|
||||
return new IpCamTimelapsCommand(parseBoolean(commandLine[0]));
|
||||
}
|
||||
|
||||
private XCamControlCommand parseXCamControlCommand(String[] commandLine) {
|
||||
requireLength(commandLine, 3);
|
||||
return new XCamControlCommand(XCamControlCommand.Module.valueOf(commandLine[0]), parseBoolean(commandLine[1]),
|
||||
parseBoolean(commandLine[2]));
|
||||
localHandler.sendCommand(stringCommand);
|
||||
}
|
||||
|
||||
public static void sendCommand(@Nullable ThingActions actions, String stringCommand) {
|
||||
|
||||
+3
-1
@@ -36,6 +36,8 @@ public class PrinterConfiguration {
|
||||
public String hostname = "";
|
||||
public String accessCode = "";
|
||||
public String username = LOCAL_USERNAME;
|
||||
public int reconnectTime = 300;
|
||||
public int reconnectMax = 5;
|
||||
// MQTT
|
||||
public String scheme = SCHEME;
|
||||
public int port = DEFAULT_PORT;
|
||||
@@ -73,7 +75,7 @@ public class PrinterConfiguration {
|
||||
return new URI(rawUri);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new InitializationException(CONFIGURATION_ERROR,
|
||||
"@text/printer.handler.init.invalidHostname[\"%s\"]".formatted(rawUri), e);
|
||||
"@text/printer.handler.init.invalidHostname [\"%s\"]".formatted(rawUri), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+319
-97
@@ -12,10 +12,17 @@
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static java.lang.Integer.parseInt;
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.Collections.synchronizedList;
|
||||
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.StateParserHelper.*;
|
||||
import static org.openhab.binding.bambulab.internal.TrayHelper.updateTrayLoaded;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
import static org.openhab.core.library.unit.Units.DECIBEL_MILLIWATTS;
|
||||
import static org.openhab.core.thing.ThingStatus.OFFLINE;
|
||||
import static org.openhab.core.thing.ThingStatus.ONLINE;
|
||||
import static org.openhab.core.thing.ThingStatus.UNKNOWN;
|
||||
@@ -27,6 +34,7 @@ import static pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PushingCommand
|
||||
import static pl.grzeslowski.jbambuapi.mqtt.PrinterClientConfig.requiredFields;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -34,28 +42,33 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
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.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
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;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
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.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import pl.grzeslowski.jbambuapi.mqtt.ConnectionCallback;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.GCodeFileCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PrintSpeedCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClientConfig;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterWatcher;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.Report;
|
||||
|
||||
@@ -66,31 +79,54 @@ import pl.grzeslowski.jbambuapi.mqtt.Report;
|
||||
* @author Martin Grześlowski - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PrinterHandler extends BaseThingHandler implements PrinterWatcher.StateSubscriber, BambuHandler {
|
||||
private static final Pattern DBM_PATTERN = Pattern.compile("^(-?\\d+)dBm$");
|
||||
public class PrinterHandler extends BaseBridgeHandler
|
||||
implements PrinterWatcher.StateSubscriber, BambuHandler, ConnectionCallback {
|
||||
private static final String INTERNAL_COMMAND_PREFIX = ">";
|
||||
private Logger logger = LoggerFactory.getLogger(PrinterHandler.class);
|
||||
|
||||
private @Nullable PrinterClient client;
|
||||
private @Nullable Camera camera;
|
||||
private final AtomicInteger reconnectTimes = new AtomicInteger();
|
||||
private final AtomicReference<@Nullable ScheduledFuture<?>> reconnectSchedule = new AtomicReference<>();
|
||||
private final PrinterWatcher printerWatcher = new PrinterWatcher();
|
||||
private @Nullable PrinterClientConfig config;
|
||||
private final Collection<AmsDeviceHandlerFactory> amses = synchronizedList(new ArrayList<>());
|
||||
private final AtomicReference<@Nullable Report> latestPrinterState = new AtomicReference<>();
|
||||
|
||||
public PrinterHandler(Thing thing) {
|
||||
super(thing);
|
||||
public PrinterHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (CHANNEL_LED_CHAMBER_LIGHT.is(channelUID) || CHANNEL_LED_WORK_LIGHT.is(channelUID)) {
|
||||
var ledNode = CHANNEL_LED_CHAMBER_LIGHT.getName().equals(channelUID.getId()) ? CHAMBER_LIGHT : WORK_LIGHT;
|
||||
var ledNode = CHANNEL_LED_CHAMBER_LIGHT.is(channelUID) ? CHAMBER_LIGHT : WORK_LIGHT;
|
||||
var bambuCommand = "ON".equals(command.toFullString()) ? on(ledNode) : off(ledNode);
|
||||
sendCommand(bambuCommand);
|
||||
} else if (CHANNEL_GCODE_FILE.is(channelUID)) {
|
||||
var bambuCommand = new GCodeFileCommand(command.toString());
|
||||
sendCommand(bambuCommand);
|
||||
} else if (CHANNEL_SPEED_LEVEL.is(channelUID)) {
|
||||
var bambuCommand = PrintSpeedCommand.valueOf(command.toString());
|
||||
sendCommand(bambuCommand);
|
||||
} else if (CHANNEL_SPEED_LEVEL.is(channelUID) && command instanceof StringType) {
|
||||
stream(PrintSpeedCommand.values())//
|
||||
.filter(type -> type.name().equalsIgnoreCase(command.toString()))//
|
||||
.findAny()//
|
||||
.ifPresent(this::sendCommand);
|
||||
} else if (CHANNEL_CAMERA_RECORD.is(channelUID) && command instanceof OnOffType onOffCommand) {
|
||||
requireNonNull(camera).handleCommand(onOffCommand);
|
||||
} else if (CHANNEL_COMMAND.is(channelUID) && command instanceof StringType) {
|
||||
var commandString = command.toString();
|
||||
if (commandString.startsWith(INTERNAL_COMMAND_PREFIX)) {
|
||||
logger.debug("Command {} updates just the state, ignoring...", commandString);
|
||||
return;
|
||||
}
|
||||
String result;
|
||||
try {
|
||||
sendCommand(commandString);
|
||||
result = INTERNAL_COMMAND_PREFIX + " SUCCESS: " + commandString;
|
||||
} catch (Exception ex) {
|
||||
result = INTERNAL_COMMAND_PREFIX + " ERROR: " + ex.getLocalizedMessage();
|
||||
}
|
||||
updateState(channelUID, StringType.valueOf(result));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +140,7 @@ public class PrinterHandler extends BaseThingHandler implements PrinterWatcher.S
|
||||
}
|
||||
|
||||
private void internalInitialize() throws InitializationException {
|
||||
PrinterConfiguration config = getConfigAs(PrinterConfiguration.class);
|
||||
var config = getConfigAs(PrinterConfiguration.class);
|
||||
|
||||
config.validateSerial();
|
||||
logger = LoggerFactory.getLogger(PrinterHandler.class.getName() + "." + config.serial);
|
||||
@@ -115,41 +151,88 @@ public class PrinterHandler extends BaseThingHandler implements PrinterWatcher.S
|
||||
|
||||
// always turn off camera recording when starting thing
|
||||
camera = new Camera(config, this);
|
||||
updateState(CHANNEL_CAMERA_RECORD.getName(), OnOffType.OFF);
|
||||
updateState(CHANNEL_CAMERA_IMAGE.getName(), UNDEF);
|
||||
updateState(CHANNEL_CAMERA_RECORD, OnOffType.OFF);
|
||||
updateState(CHANNEL_CAMERA_IMAGE, UNDEF);
|
||||
|
||||
// turn off the command channel
|
||||
updateState(CHANNEL_COMMAND, StringType.valueOf(INTERNAL_COMMAND_PREFIX + " Ready to accept commands"));
|
||||
|
||||
var localClient = client = buildLocalClient(uri, config);
|
||||
|
||||
// the status will be unknown until the first message form MQTT arrives
|
||||
updateStatus(UNKNOWN);
|
||||
try {
|
||||
scheduler.execute(() -> initMqtt(localClient));
|
||||
scheduler.execute(() -> initMqtt(localClient, config));
|
||||
} catch (RejectedExecutionException ex) {
|
||||
logger.debug("Task was rejected", ex);
|
||||
throw new InitializationException(CONFIGURATION_ERROR, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void initMqtt(PrinterClient client) {
|
||||
private void initMqtt(PrinterClient client, PrinterConfiguration config) {
|
||||
try {
|
||||
logger.debug("Trying to connect to the printer broker");
|
||||
client.connect();
|
||||
var printerWatcher = new PrinterWatcher();
|
||||
client.connect(this);
|
||||
client.subscribe(printerWatcher);
|
||||
printerWatcher.subscribe(this);
|
||||
// send request to update all channels
|
||||
refreshChannels();
|
||||
updateStatus(ONLINE);
|
||||
// update to online done in `connectComplete`
|
||||
} catch (Exception e) {
|
||||
logger.debug("Cannot connect to MQTT client", e);
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, e.getLocalizedMessage());
|
||||
reconnect(config);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectComplete(boolean reconnect) {
|
||||
logger.debug("connectComplete({})", reconnect);
|
||||
// send request to update all channels
|
||||
scheduler.schedule(this::refreshChannels, 1, SECONDS);
|
||||
reconnectTimes.set(0);
|
||||
updateStatus(ONLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionLost(@Nullable Throwable throwable) {
|
||||
logger.debug("Connection lost. Restarting thing", throwable);
|
||||
var message = throwable != null ? throwable.getLocalizedMessage() : "<no message" + INTERNAL_COMMAND_PREFIX;
|
||||
var description = "@text/printer.handler.init.connectionLost [\"%s\"]".formatted(message);
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, description);
|
||||
reconnect(null);
|
||||
}
|
||||
|
||||
private void reconnect(@Nullable PrinterConfiguration config) {
|
||||
if (reconnectSchedule.get() != null) {
|
||||
logger.warn("Already reconnecting!");
|
||||
return;
|
||||
}
|
||||
var configNotNull = Optional.ofNullable(config)//
|
||||
.orElseGet(() -> getConfigAs(PrinterConfiguration.class));
|
||||
var currentReconnections = reconnectTimes.getAndAdd(1);
|
||||
if (currentReconnections >= configNotNull.reconnectMax) {
|
||||
logger.warn("Do not reconnecting any more, because max reconnections was reach!");
|
||||
return;
|
||||
}
|
||||
logger.debug("Will reconnect in {} seconds...", configNotNull.reconnectTime);
|
||||
try {
|
||||
reconnectSchedule.set(//
|
||||
scheduler.schedule(() -> {
|
||||
logger.debug("Reconnecting...");
|
||||
reconnectSchedule.set(null);
|
||||
dispose();
|
||||
initialize();
|
||||
}, configNotNull.reconnectTime, SECONDS));
|
||||
} catch (RejectedExecutionException ex) {
|
||||
logger.debug("Task was rejected", ex);
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private PrinterClient buildLocalClient(URI uri, PrinterConfiguration config) throws InitializationException {
|
||||
try {
|
||||
return new PrinterClient(
|
||||
requiredFields(uri, config.username, config.serial, config.accessCode.toCharArray()));
|
||||
var localConfig = this.config = requiredFields(uri, config.username, config.serial,
|
||||
config.accessCode.toCharArray());
|
||||
return new PrinterClient(localConfig);
|
||||
} catch (Exception e) {
|
||||
logger.debug("Cannot create MQTT client", e);
|
||||
throw new InitializationException(COMMUNICATION_ERROR, e);
|
||||
@@ -163,28 +246,60 @@ public class PrinterHandler extends BaseThingHandler implements PrinterWatcher.S
|
||||
@Override
|
||||
public void dispose() {
|
||||
try {
|
||||
var localClient = client;
|
||||
client = null;
|
||||
if (localClient != null) {
|
||||
try {
|
||||
localClient.close();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Could not correctly dispose PrinterClient", e);
|
||||
}
|
||||
}
|
||||
var localCamera = camera;
|
||||
camera = null;
|
||||
if (localCamera != null) {
|
||||
localCamera.close();
|
||||
}
|
||||
latestPrinterState.set(null);
|
||||
printerWatcher.close();
|
||||
amses.clear();
|
||||
closeConfig();
|
||||
closeReconnectSchedule();
|
||||
closeCamera();
|
||||
closeClient();
|
||||
} finally {
|
||||
logger = LoggerFactory.getLogger(PrinterHandler.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeConfig() {
|
||||
var localConfig = config;
|
||||
config = null;
|
||||
if (localConfig != null) {
|
||||
localConfig.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void closeClient() {
|
||||
var localClient = client;
|
||||
client = null;
|
||||
if (localClient != null) {
|
||||
try {
|
||||
localClient.close();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Could not correctly dispose PrinterClient", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeCamera() {
|
||||
var localCamera = camera;
|
||||
camera = null;
|
||||
if (localCamera != null) {
|
||||
localCamera.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void closeReconnectSchedule() {
|
||||
var localReconnectSchedule = reconnectSchedule.get();
|
||||
reconnectSchedule.set(null);
|
||||
if (localReconnectSchedule != null) {
|
||||
localReconnectSchedule.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newState(@Nullable Report delta, @Nullable Report fullState) {
|
||||
logger.trace("New Printer state from delta {}", delta);
|
||||
if (fullState != null) {
|
||||
latestPrinterState.set(fullState);
|
||||
}
|
||||
// only need to update channels from delta
|
||||
// do not need to use full state, because at some point in past channels was already updated with its values
|
||||
if (delta == null) {
|
||||
@@ -202,66 +317,159 @@ public class PrinterHandler extends BaseThingHandler implements PrinterWatcher.S
|
||||
return;
|
||||
}
|
||||
// tempers
|
||||
updateCelsiusState(CHANNEL_NOZZLE_TEMPERATURE.getName(), print.nozzleTemper());
|
||||
updateCelsiusState(CHANNEL_NOZZLE_TARGET_TEMPERATURE.getName(), print.nozzleTargetTemper());
|
||||
updateCelsiusState(CHANNEL_BED_TEMPERATURE.getName(), print.bedTemper());
|
||||
updateCelsiusState(CHANNEL_BED_TARGET_TEMPERATURE.getName(), print.bedTargetTemper());
|
||||
updateCelsiusState(CHANNEL_CHAMBER_TEMPERATURE.getName(), print.chamberTemper());
|
||||
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.getName(), print.mcPrintStage());
|
||||
updateStringState(CHANNEL_BED_TYPE.getName(), print.bedType());
|
||||
updateStringState(CHANNEL_GCODE_FILE.getName(), print.gcodeFile());
|
||||
updateStringState(CHANNEL_GCODE_STATE.getName(), print.gcodeState());
|
||||
updateStringState(CHANNEL_REASON.getName(), print.reason());
|
||||
updateStringState(CHANNEL_RESULT.getName(), print.result());
|
||||
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.getName(), print.mcPercent());
|
||||
updatePercentState(CHANNEL_GCODE_FILE_PREPARE_PERCENT.getName(), print.gcodeFilePreparePercent());
|
||||
updatePercentState(CHANNEL_MC_PERCENT, print.mcPercent());
|
||||
updatePercentState(CHANNEL_GCODE_FILE_PREPARE_PERCENT, print.gcodeFilePreparePercent());
|
||||
// decimal
|
||||
updateDecimalState(CHANNEL_MC_REMAINING_TIME.getName(), print.mcRemainingTime());
|
||||
updateDecimalState(CHANNEL_BIG_FAN_1_SPEED.getName(), print.bigFan1Speed());
|
||||
updateDecimalState(CHANNEL_BIG_FAN_2_SPEED.getName(), print.bigFan2Speed());
|
||||
updateDecimalState(CHANNEL_HEAT_BREAK_FAN_SPEED.getName(), print.heatbreakFanSpeed());
|
||||
updateDecimalState(CHANNEL_LAYER_NUM.getName(), print.layerNum());
|
||||
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.getName(), new StringType(speedLevel.toString()));
|
||||
updateState(CHANNEL_SPEED_LEVEL, new StringType(speedLevel.toString()));
|
||||
}
|
||||
// boolean
|
||||
updateBooleanState(CHANNEL_TIME_LAPS.getName(), print.timelapse());
|
||||
updateBooleanState(CHANNEL_USE_AMS.getName(), print.useAms());
|
||||
updateBooleanState(CHANNEL_VIBRATION_CALIBRATION.getName(), print.vibrationCali());
|
||||
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.getName(), parseWifiChannel(print.wifiSignal()));
|
||||
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 updateCelsiusState(String channelId, @Nullable Double temperature) {
|
||||
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 updateAms(Map<String, Object> amsMap) {
|
||||
Optional.of(amsMap)//
|
||||
.map(map -> map.get("id"))//
|
||||
.map(Object::toString)//
|
||||
.map(Integer::parseInt)//
|
||||
// in code, we are using 1-4 ordering; in API 0-3 ordering is used
|
||||
.map(id -> id + 1) //
|
||||
.filter(id -> id >= MIN_AMS)//
|
||||
.filter(id -> id <= MAX_AMS)//
|
||||
.map(id -> amses.stream().filter(ams -> ams.getAmsNumber() == id))//
|
||||
.stream()//
|
||||
.flatMap(identity())//
|
||||
.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(String channelId, @Nullable String string) {
|
||||
private void updateStringState(BambuLabBindingConstants.Channel channelId, @Nullable String string) {
|
||||
if (string == null) {
|
||||
return;
|
||||
}
|
||||
updateState(channelId, new StringType(string));
|
||||
}
|
||||
|
||||
private void updateDecimalState(String channelId, @Nullable Number number) {
|
||||
private void updateDecimalState(BambuLabBindingConstants.Channel channelId, @Nullable Number number) {
|
||||
if (number == null) {
|
||||
return;
|
||||
}
|
||||
updateState(channelId, new DecimalType(number));
|
||||
}
|
||||
|
||||
private void updateDecimalState(String channelId, @Nullable String number) {
|
||||
private void updateDecimalState(BambuLabBindingConstants.Channel channelId, @Nullable String number) {
|
||||
if (number == null) {
|
||||
return;
|
||||
}
|
||||
@@ -274,31 +482,19 @@ public class PrinterHandler extends BaseThingHandler implements PrinterWatcher.S
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBooleanState(String channelId, @Nullable Boolean bool) {
|
||||
private void updateBooleanState(BambuLabBindingConstants.Channel channelId, @Nullable Boolean bool) {
|
||||
if (bool == null) {
|
||||
return;
|
||||
}
|
||||
updateState(channelId, OnOffType.from(bool));
|
||||
}
|
||||
|
||||
private void updatePercentState(String channelId, @Nullable Integer integer) {
|
||||
if (integer == null) {
|
||||
return;
|
||||
}
|
||||
updateState(channelId, new PercentType(integer));
|
||||
private void updatePercentState(BambuLabBindingConstants.Channel channelId, @Nullable Integer integer) {
|
||||
parsePercentType(integer).ifPresent(state -> updateState(channelId, state));
|
||||
}
|
||||
|
||||
private void updatePercentState(String channelId, @Nullable String integer) {
|
||||
if (integer == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var state = new PercentType(integer);
|
||||
updateState(channelId, state);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Cannot parse percent number {}", integer, e);
|
||||
updateState(channelId, UNDEF);
|
||||
}
|
||||
private void updatePercentState(BambuLabBindingConstants.Channel channelId, @Nullable String integer) {
|
||||
parsePercentType(integer).ifPresent(state -> updateState(channelId, state));
|
||||
}
|
||||
|
||||
private void updateLightState(String lightName, BambuLabBindingConstants.Channel channel,
|
||||
@@ -311,23 +507,11 @@ public class PrinterHandler extends BaseThingHandler implements PrinterWatcher.S
|
||||
.filter(Objects::nonNull)//
|
||||
.map(OnOffType::from)//
|
||||
.findAny()//
|
||||
.ifPresent(command -> updateState(channel.getName(), command));
|
||||
.ifPresent(command -> updateState(channel, command));
|
||||
}
|
||||
|
||||
private State parseWifiChannel(String wifi) {
|
||||
var matcher = DBM_PATTERN.matcher(wifi);
|
||||
if (!matcher.matches()) {
|
||||
return UNDEF;
|
||||
}
|
||||
|
||||
var integer = matcher.group(1);
|
||||
try {
|
||||
var value = Integer.parseInt(integer);
|
||||
return new QuantityType<>(value, DECIBEL_MILLIWATTS);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Cannot parse integer {} from wifi {}", integer, wifi, e);
|
||||
return UNDEF;
|
||||
}
|
||||
public void sendCommand(String command) {
|
||||
sendCommand(CommandParser.parseCommand(command));
|
||||
}
|
||||
|
||||
public void sendCommand(PrinterClient.Channel.Command command) {
|
||||
@@ -344,6 +528,10 @@ public class PrinterHandler extends BaseThingHandler implements PrinterWatcher.S
|
||||
}
|
||||
}
|
||||
|
||||
private void updateState(BambuLabBindingConstants.Channel channelID, State state) {
|
||||
updateState(channelID.getName(), state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
|
||||
super.updateStatus(status, statusDetail, description);
|
||||
@@ -368,4 +556,38 @@ public class PrinterHandler extends BaseThingHandler implements PrinterWatcher.S
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) {
|
||||
if (!(childHandler instanceof AmsDeviceHandlerFactory ams)) {
|
||||
return;
|
||||
}
|
||||
amses.add(ams);
|
||||
Optional.of(latestPrinterState)//
|
||||
.map(AtomicReference::get)//
|
||||
.map(Report::print).map(Report.Print::ams)//
|
||||
.map(Report.Print.Ams::ams)//
|
||||
.stream()//
|
||||
.flatMap(Collection::stream)//
|
||||
.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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) {
|
||||
if (!(childHandler instanceof AmsDeviceHandlerFactory ams)) {
|
||||
return;
|
||||
}
|
||||
var removed = amses.remove(ams);
|
||||
if (!removed) {
|
||||
logger.warn("Did not remove ams handler {}", ams);
|
||||
}
|
||||
}
|
||||
|
||||
public String getSerialNumber() {
|
||||
return requireNonNull(config).serial();
|
||||
}
|
||||
}
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static java.lang.Integer.parseInt;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
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.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.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.util.ColorUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Martin Grześlowski - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class StateParserHelper {
|
||||
private static final Pattern DBM_PATTERN = Pattern.compile("^(-?\\d+)dBm$");
|
||||
private static final Logger logger = LoggerFactory.getLogger(StateParserHelper.class);
|
||||
|
||||
public static Optional<State> parseTimeMinutes(@Nullable Integer integer) {
|
||||
return Optional.ofNullable(integer)//
|
||||
.map(time -> new QuantityType<>(time, MINUTE));
|
||||
}
|
||||
|
||||
public static Optional<State> parseTemperatureType(@Nullable String value) {
|
||||
try {
|
||||
return Optional.ofNullable(value).map(Double::parseDouble).map(d -> new QuantityType<>(d, CELSIUS));
|
||||
} catch (NumberFormatException ex) {
|
||||
logger.debug("Cannot parse: {}", value, ex);
|
||||
return Optional.of(UNDEF);
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<State> parseDecimalType(@Nullable Number number) {
|
||||
return Optional.ofNullable(number).map(DecimalType::new);
|
||||
}
|
||||
|
||||
public static Optional<State> parseDecimalType(@Nullable String value) {
|
||||
try {
|
||||
return Optional.ofNullable(value).map(DecimalType::new);
|
||||
} catch (NumberFormatException ex) {
|
||||
logger.debug("Cannot parse {}", value, ex);
|
||||
return Optional.of(UNDEF);
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<State> parsePercentType(@Nullable Number percent) {
|
||||
return Optional.ofNullable(percent).map(d -> new QuantityType<>(d, PERCENT));
|
||||
}
|
||||
|
||||
public static Optional<State> parsePercentType(@Nullable String percent) {
|
||||
try {
|
||||
return Optional.ofNullable(percent)//
|
||||
.map(Double::parseDouble)//
|
||||
.map(d -> new QuantityType<>(d, PERCENT));
|
||||
} catch (NumberFormatException ex) {
|
||||
logger.debug("Cannot parse {}", percent, ex);
|
||||
return Optional.of(UNDEF);
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<State> parseStringType(@Nullable String string) {
|
||||
return Optional.ofNullable(string).map(StringType::new);
|
||||
}
|
||||
|
||||
public static State parseWifiChannel(String wifi) {
|
||||
var matcher = DBM_PATTERN.matcher(wifi);
|
||||
if (!matcher.matches()) {
|
||||
logger.debug("Cannot match {} to {}", wifi, DBM_PATTERN);
|
||||
return UNDEF;
|
||||
}
|
||||
|
||||
var integer = matcher.group(1);
|
||||
try {
|
||||
var value = parseInt(integer);
|
||||
return new QuantityType<>(value, DECIBEL_MILLIWATTS);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Cannot parse integer {} from wifi {}", integer, wifi, e);
|
||||
return UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
public static State parseColor(String colorHex) {
|
||||
if (colorHex.length() < 6) {
|
||||
return UNDEF;
|
||||
}
|
||||
int r = parseInt(colorHex.substring(0, 2), 16);
|
||||
int g = parseInt(colorHex.substring(2, 4), 16);
|
||||
int b = parseInt(colorHex.substring(4, 6), 16);
|
||||
return ColorUtil.rgbToHsb(new int[] { r, g, b });
|
||||
}
|
||||
|
||||
public static Optional<State> undef() {
|
||||
return Optional.of(UNDEF);
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static java.lang.Integer.parseInt;
|
||||
import static org.openhab.binding.bambulab.internal.BambuLabBindingConstants.AmsChannel.*;
|
||||
import static org.openhab.core.types.UnDefType.UNDEF;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Martin Grześlowski - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
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) {
|
||||
if (tray == null) {
|
||||
return UNDEF;
|
||||
}
|
||||
try {
|
||||
var integer = parseInt(tray);
|
||||
return findStateForTrayLoaded(integer);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Cannot parse: {}", tray, e);
|
||||
return UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
private static State findStateForTrayLoaded(int tray) {
|
||||
if (tray < 0) {
|
||||
return UNDEF;
|
||||
}
|
||||
if (tray == 255) {
|
||||
return StringType.valueOf("EMPTY");
|
||||
}
|
||||
if (tray == 254) {
|
||||
return StringType.valueOf("VTRAY");
|
||||
}
|
||||
if (tray > MAX_TRAY_VALUE) {
|
||||
logger.warn("There should never be tray with value {}", tray);
|
||||
return UNDEF;
|
||||
}
|
||||
var amsNr = (tray / MAX_AMS) + 1;
|
||||
var trayNr = (tray % MAX_AMS_TRAYS) + 1;
|
||||
return StringType.valueOf("AMS_%s_%s".formatted(amsNr, trayNr));
|
||||
}
|
||||
}
|
||||
+728
-7
@@ -5,8 +5,15 @@ addon.bambulab.description = This is the binding for BambuLab.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.bambulab.ams-device.label = Bambu Lab AMS Device
|
||||
thing-type.bambulab.ams-device.description = Represents a Bambu Lab AMS connected to a printer.
|
||||
thing-type.bambulab.ams-device.group.ams-tray-1.label = AMS Tray #1
|
||||
thing-type.bambulab.ams-device.group.ams-tray-2.label = AMS Tray #2
|
||||
thing-type.bambulab.ams-device.group.ams-tray-3.label = AMS Tray #3
|
||||
thing-type.bambulab.ams-device.group.ams-tray-4.label = AMS Tray #4
|
||||
thing-type.bambulab.printer.label = Bambu Lab Printer
|
||||
thing-type.bambulab.printer.description = Represents a Bambu Lab 3D printer connected via MQTT
|
||||
thing-type.bambulab.printer.channel.ams-tray-previous.label = Previously Loaded Tray
|
||||
thing-type.bambulab.printer.channel.bed-target-temperature.label = Bed Target Temperature
|
||||
thing-type.bambulab.printer.channel.bed-target-temperature.description = Target temperature of the heated bed.
|
||||
thing-type.bambulab.printer.channel.bed-temperature.label = Bed Temperature
|
||||
@@ -21,6 +28,8 @@ thing-type.bambulab.printer.channel.camera-record.label = Camera Record
|
||||
thing-type.bambulab.printer.channel.camera-record.description = Turns on/off openHAB's capability of recording.
|
||||
thing-type.bambulab.printer.channel.chamber-temperature.label = Chamber Temperature
|
||||
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 <a href="https://github.com/openhab/openhab-addons/tree/main/bundles/org.openhab.binding.bambulab#sendcommand">Actions > sendCommand</a>
|
||||
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
|
||||
@@ -55,11 +64,31 @@ thing-type.bambulab.printer.channel.use-ams.label = AMS System in Use
|
||||
thing-type.bambulab.printer.channel.use-ams.description = Indicates whether the Automatic Material System (AMS) is active.
|
||||
thing-type.bambulab.printer.channel.vibration-calibration.label = Vibration Calibration
|
||||
thing-type.bambulab.printer.channel.vibration-calibration.description = Indicates whether vibration calibration has been performed.
|
||||
thing-type.bambulab.printer.channel.vtray-bed-temp-type.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.vtray-bed-temperature.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.vtray-k.label = k
|
||||
thing-type.bambulab.printer.channel.vtray-n.label = n
|
||||
thing-type.bambulab.printer.channel.vtray-nozzle-temperature-max.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.vtray-nozzle-temperature-max.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.vtray-nozzle-temperature-min.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.vtray-nozzle-temperature-min.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.vtray-remain.label = Remain
|
||||
thing-type.bambulab.printer.channel.vtray-remain.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.vtray-tag-uuid.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.vtray-tray-diameter.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.vtray-tray-id-name.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.vtray-tray-info-idx.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.vtray-tray-sub-brands.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.vtray-tray-temperature.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.vtray-tray-time.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.vtray-tray-weight.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.wifi-signal.label = WiFi Signal Strength
|
||||
thing-type.bambulab.printer.channel.wifi-signal.description = Current WiFi signal strength.
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.bambulab.ams-device.number.label = AMS Number
|
||||
thing-type.config.bambulab.ams-device.number.description = Order of the AMS
|
||||
thing-type.config.bambulab.printer.accessCode.label = Access Code
|
||||
thing-type.config.bambulab.printer.accessCode.description = Access code of the printer. The method of obtaining it differs for local and cloud modes.
|
||||
thing-type.config.bambulab.printer.cameraCertificate.label = TLS Certificate
|
||||
@@ -74,6 +103,10 @@ thing-type.config.bambulab.printer.hostname.label = Hostname
|
||||
thing-type.config.bambulab.printer.hostname.description = IP address of the printer or `us.mqtt.bambulab.com` for cloud mode.
|
||||
thing-type.config.bambulab.printer.port.label = Port
|
||||
thing-type.config.bambulab.printer.port.description = URI port.
|
||||
thing-type.config.bambulab.printer.reconnectMax.label = Max Reconnections
|
||||
thing-type.config.bambulab.printer.reconnectMax.description = Max times to try reconnections.
|
||||
thing-type.config.bambulab.printer.reconnectTime.label = Reconnect Time
|
||||
thing-type.config.bambulab.printer.reconnectTime.description = Time in seconds to wait before reconnect.
|
||||
thing-type.config.bambulab.printer.scheme.label = Scheme
|
||||
thing-type.config.bambulab.printer.scheme.description = URI scheme.
|
||||
thing-type.config.bambulab.printer.serial.label = Serial Number
|
||||
@@ -86,18 +119,38 @@ thing-type.config.bambulab.printer.series.option.X = X Series
|
||||
thing-type.config.bambulab.printer.username.label = Username
|
||||
thing-type.config.bambulab.printer.username.description = `bblp` for local mode or your Bambu Lab user (starts with `u_`).
|
||||
|
||||
# channel group types
|
||||
|
||||
channel-group-type.bambulab.ams-tray.label = AMS Tray
|
||||
channel-group-type.bambulab.ams-tray.description = This channel group represents a single AMS tray.
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-bed-temp-type.label = Bed Temperature Type
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-bed-temperature.label = Bed Temperature
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-ctype.label = ctype
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-k.label = k
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-n.label = n
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-nozzle-temperature-max.label = Nozzle Temperature Max
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-nozzle-temperature-max.description = Maximum nozzle temperature for tray.
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-nozzle-temperature-min.label = Nozzle Temperature Min
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-nozzle-temperature-min.description = Minimum nozzle temperature for tray.
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-remain.label = Remain
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-remain.description = Amount of remaining filament.
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-tag-uuid.label = Tag UUID
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-tray-diameter.label = Tray Diameter
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-tray-id-name.label = Tray ID Name
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-tray-info-idx.label = Tray Info IDX Name
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-tray-sub-brands.label = Tray Sub Brands
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-tray-temperature.label = Tray Temperature
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-tray-time.label = Tray Time
|
||||
channel-group-type.bambulab.ams-tray.channel.ams-tray-weight.label = Tray Weight
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.bambulab.boolean-rw.label = Boolean Channel
|
||||
channel-type.bambulab.boolean.label = Boolean Channel
|
||||
channel-type.bambulab.camera-image.label = Camera Image
|
||||
channel-type.bambulab.camera-image.description = Current image from the printer's camera.
|
||||
channel-type.bambulab.number-advanced.label = Advanced Number Channel
|
||||
channel-type.bambulab.number.label = Number Channel
|
||||
channel-type.bambulab.on-off-command.label = ON/OFF Channel
|
||||
channel-type.bambulab.on-off-command.state.option.ON = ON
|
||||
channel-type.bambulab.on-off-command.state.option.OFF = OFF
|
||||
channel-type.bambulab.on-off-command.command.option.ON = ON
|
||||
channel-type.bambulab.on-off-command.command.option.OFF = OFF
|
||||
channel-type.bambulab.percent.label = Percent Channel
|
||||
channel-type.bambulab.speed-level.label = Print Speed Level
|
||||
channel-type.bambulab.speed-level.description = Current speed setting of the print job.
|
||||
@@ -109,18 +162,687 @@ channel-type.bambulab.speed-level.command.option.SILENT = Silent
|
||||
channel-type.bambulab.speed-level.command.option.STANDARD = Standard
|
||||
channel-type.bambulab.speed-level.command.option.SPORT = Sport
|
||||
channel-type.bambulab.speed-level.command.option.LUDICROUS = Ludicrous
|
||||
channel-type.bambulab.string-advanced.label = Advanced String Channel
|
||||
channel-type.bambulab.string-rw.label = RW String Channel
|
||||
channel-type.bambulab.string.label = String Channel
|
||||
channel-type.bambulab.temperature-measurement-advanced.label = Current Temperature
|
||||
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.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
|
||||
channel-type.bambulab.tray-loaded.state.option.VTRAY = External Spool
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_1_1 = AMS #1 / Tray 1
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_1_2 = AMS #1 / Tray 2
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_1_3 = AMS #1 / Tray 3
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_1_4 = AMS #1 / Tray 4
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_2_1 = AMS #2 / Tray 1
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_2_2 = AMS #2 / Tray 2
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_2_3 = AMS #2 / Tray 3
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_2_4 = AMS #2 / Tray 4
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_3_1 = AMS #3 / Tray 1
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_3_2 = AMS #3 / Tray 2
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_3_3 = AMS #3 / Tray 3
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_3_4 = AMS #3 / Tray 4
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_4_1 = AMS #4 / Tray 1
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_4_2 = AMS #4 / Tray 2
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_4_3 = AMS #4 / Tray 3
|
||||
channel-type.bambulab.tray-loaded.state.option.AMS_4_4 = AMS #4 / Tray 4
|
||||
channel-type.bambulab.tray-type.label = Tray Type
|
||||
channel-type.bambulab.tray-type.state.option.PLA = PLA
|
||||
channel-type.bambulab.tray-type.state.option.PETG = PETG
|
||||
channel-type.bambulab.tray-type.state.option.ABS = ABS
|
||||
channel-type.bambulab.tray-type.state.option.TPU = TPU
|
||||
channel-type.bambulab.tray-type.state.option.ASA = ASA
|
||||
channel-type.bambulab.tray-type.state.option.PA = PA
|
||||
channel-type.bambulab.tray-type.state.option.PC = PC
|
||||
channel-type.bambulab.tray-type.state.option.PVA = PVA
|
||||
channel-type.bambulab.tray-type.state.option.HIPS = HIPS
|
||||
channel-type.bambulab.wifi.label = Wi-Fi Signal Strength
|
||||
channel-type.bambulab.wifi.description = Current Wi-Fi signal strength.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.bambulab.ams-device.channel.ams-bed-temp-type-id-1.label = Bed Temperature Type
|
||||
thing-type.bambulab.ams-device.channel.ams-bed-temp-type-id-2.label = Bed Temperature Type
|
||||
thing-type.bambulab.ams-device.channel.ams-bed-temp-type-id-3.label = Bed Temperature Type
|
||||
thing-type.bambulab.ams-device.channel.ams-bed-temp-type-id-4.label = Bed Temperature Type
|
||||
thing-type.bambulab.ams-device.channel.ams-bed-temperature-id-1.label = Bed Temperature
|
||||
thing-type.bambulab.ams-device.channel.ams-bed-temperature-id-2.label = Bed Temperature
|
||||
thing-type.bambulab.ams-device.channel.ams-bed-temperature-id-3.label = Bed Temperature
|
||||
thing-type.bambulab.ams-device.channel.ams-bed-temperature-id-4.label = Bed Temperature
|
||||
thing-type.bambulab.ams-device.channel.ams-ctype-id-1.label = ctype
|
||||
thing-type.bambulab.ams-device.channel.ams-ctype-id-2.label = ctype
|
||||
thing-type.bambulab.ams-device.channel.ams-ctype-id-3.label = ctype
|
||||
thing-type.bambulab.ams-device.channel.ams-ctype-id-4.label = ctype
|
||||
thing-type.bambulab.ams-device.channel.ams-k-id-1.label = k
|
||||
thing-type.bambulab.ams-device.channel.ams-k-id-2.label = k
|
||||
thing-type.bambulab.ams-device.channel.ams-k-id-3.label = k
|
||||
thing-type.bambulab.ams-device.channel.ams-k-id-4.label = k
|
||||
thing-type.bambulab.ams-device.channel.ams-n-id-1.label = n
|
||||
thing-type.bambulab.ams-device.channel.ams-n-id-2.label = n
|
||||
thing-type.bambulab.ams-device.channel.ams-n-id-3.label = n
|
||||
thing-type.bambulab.ams-device.channel.ams-n-id-4.label = n
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-max-id-1.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-max-id-1.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-max-id-2.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-max-id-2.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-max-id-3.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-max-id-3.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-max-id-4.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-max-id-4.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-min-id-1.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-min-id-1.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-min-id-2.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-min-id-2.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-min-id-3.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-min-id-3.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-min-id-4.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.ams-device.channel.ams-nozzle-temperature-min-id-4.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams-device.channel.ams-remain-id-1.label = Remain
|
||||
thing-type.bambulab.ams-device.channel.ams-remain-id-1.description = Amount of remaining filament.
|
||||
thing-type.bambulab.ams-device.channel.ams-remain-id-2.label = Remain
|
||||
thing-type.bambulab.ams-device.channel.ams-remain-id-2.description = Amount of remaining filament.
|
||||
thing-type.bambulab.ams-device.channel.ams-remain-id-3.label = Remain
|
||||
thing-type.bambulab.ams-device.channel.ams-remain-id-3.description = Amount of remaining filament.
|
||||
thing-type.bambulab.ams-device.channel.ams-remain-id-4.label = Remain
|
||||
thing-type.bambulab.ams-device.channel.ams-remain-id-4.description = Amount of remaining filament.
|
||||
thing-type.bambulab.ams-device.channel.ams-tag-uuid-id-1.label = Tag UUID
|
||||
thing-type.bambulab.ams-device.channel.ams-tag-uuid-id-2.label = Tag UUID
|
||||
thing-type.bambulab.ams-device.channel.ams-tag-uuid-id-3.label = Tag UUID
|
||||
thing-type.bambulab.ams-device.channel.ams-tag-uuid-id-4.label = Tag UUID
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-diameter-id-1.label = Tray Diameter
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-diameter-id-2.label = Tray Diameter
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-diameter-id-3.label = Tray Diameter
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-diameter-id-4.label = Tray Diameter
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-id-name-id-1.label = Tray ID Name
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-id-name-id-2.label = Tray ID Name
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-id-name-id-3.label = Tray ID Name
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-id-name-id-4.label = Tray ID Name
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-info-idx-id-1.label = Tray Info IDX Name
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-info-idx-id-2.label = Tray Info IDX Name
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-info-idx-id-3.label = Tray Info IDX Name
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-info-idx-id-4.label = Tray Info IDX Name
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-sub-brands-id-1.label = Tray Sub Brands
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-sub-brands-id-2.label = Tray Sub Brands
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-sub-brands-id-3.label = Tray Sub Brands
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-sub-brands-id-4.label = Tray Sub Brands
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-temperature-id-1.label = Tray Temperature
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-temperature-id-2.label = Tray Temperature
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-temperature-id-3.label = Tray Temperature
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-temperature-id-4.label = Tray Temperature
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-time-id-1.label = Tray Time
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-time-id-2.label = Tray Time
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-time-id-3.label = Tray Time
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-time-id-4.label = Tray Time
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-weight-id-1.label = Tray Weight
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-weight-id-2.label = Tray Weight
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-weight-id-3.label = Tray Weight
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-weight-id-4.label = Tray Weight
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.bambulab.ams-device.channel.ams-tray-previous.label = Previously Loaded Tray
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.bambulab.on-off-command.label = ON/OFF Channel
|
||||
channel-type.bambulab.on-off-command.state.option.ON = ON
|
||||
channel-type.bambulab.on-off-command.state.option.OFF = OFF
|
||||
channel-type.bambulab.on-off-command.command.option.ON = ON
|
||||
channel-type.bambulab.on-off-command.command.option.OFF = OFF
|
||||
channel-type.bambulab.tray-type.command.option.PLA = PLA
|
||||
channel-type.bambulab.tray-type.command.option.PETG = PETG
|
||||
channel-type.bambulab.tray-type.command.option.ABS = ABS
|
||||
channel-type.bambulab.tray-type.command.option.TPU = TPU
|
||||
channel-type.bambulab.tray-type.command.option.ASA = ASA
|
||||
channel-type.bambulab.tray-type.command.option.PA = PA
|
||||
channel-type.bambulab.tray-type.command.option.PC = PC
|
||||
channel-type.bambulab.tray-type.command.option.PVA = PVA
|
||||
channel-type.bambulab.tray-type.command.option.HIPS = HIPS
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-1x1.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-1x2.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-1x3.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-1x4.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-2x1.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-2x2.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-2x3.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-2x4.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-3x1.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-3x2.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-3x3.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-3x4.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-4x1.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-4x2.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-4x3.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-4x4.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-1x1.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-1x2.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-1x3.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-1x4.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-2x1.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-2x2.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-2x3.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-2x4.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-3x1.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-3x2.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-3x3.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-3x4.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-4x1.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-4x2.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-4x3.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-4x4.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-1x1.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-1x2.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-1x3.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-1x4.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-2x1.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-2x2.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-2x3.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-2x4.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-3x1.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-3x2.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-3x3.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-3x4.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-4x1.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-4x2.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-4x3.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-4x4.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-k-id-1x1.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-1x2.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-1x3.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-1x4.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-2x1.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-2x2.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-2x3.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-2x4.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-3x1.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-3x2.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-3x3.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-3x4.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-4x1.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-4x2.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-4x3.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-4x4.label = k
|
||||
thing-type.bambulab.printer.channel.ams-n-id-1x1.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-1x2.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-1x3.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-1x4.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-2x1.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-2x2.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-2x3.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-2x4.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-3x1.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-3x2.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-3x3.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-3x4.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-4x1.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-4x2.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-4x3.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-4x4.label = n
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-1x1.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-1x1.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-1x2.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-1x2.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-1x3.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-1x3.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-1x4.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-1x4.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-2x1.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-2x1.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-2x2.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-2x2.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-2x3.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-2x3.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-2x4.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-2x4.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-3x1.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-3x1.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-3x2.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-3x2.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-3x3.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-3x3.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-3x4.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-3x4.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-4x1.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-4x1.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-4x2.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-4x2.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-4x3.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-4x3.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-4x4.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-4x4.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-1x1.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-1x1.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-1x2.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-1x2.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-1x3.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-1x3.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-1x4.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-1x4.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-2x1.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-2x1.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-2x2.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-2x2.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-2x3.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-2x3.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-2x4.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-2x4.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-3x1.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-3x1.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-3x2.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-3x2.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-3x3.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-3x3.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-3x4.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-3x4.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-4x1.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-4x1.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-4x2.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-4x2.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-4x3.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-4x3.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-4x4.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-4x4.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-1x1.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-1x1.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-1x2.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-1x2.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-1x3.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-1x3.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-1x4.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-1x4.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-2x1.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-2x1.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-2x2.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-2x2.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-2x3.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-2x3.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-2x4.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-2x4.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-3x1.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-3x1.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-3x2.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-3x2.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-3x3.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-3x3.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-3x4.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-3x4.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-4x1.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-4x1.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-4x2.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-4x2.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-4x3.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-4x3.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-4x4.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-4x4.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-1x1.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-1x2.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-1x3.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-1x4.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-2x1.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-2x2.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-2x3.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-2x4.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-3x1.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-3x2.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-3x3.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-3x4.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-4x1.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-4x2.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-4x3.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-4x4.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-1x1.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-1x2.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-1x3.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-1x4.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-2x1.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-2x2.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-2x3.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-2x4.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-3x1.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-3x2.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-3x3.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-3x4.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-4x1.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-4x2.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-4x3.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-4x4.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-1x1.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-1x2.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-1x3.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-1x4.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-2x1.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-2x2.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-2x3.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-2x4.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-3x1.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-3x2.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-3x3.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-3x4.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-4x1.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-4x2.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-4x3.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-4x4.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-1x1.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-1x2.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-1x3.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-1x4.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-2x1.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-2x2.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-2x3.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-2x4.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-3x1.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-3x2.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-3x3.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-3x4.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-4x1.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-4x2.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-4x3.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-4x4.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-1x1.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-1x2.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-1x3.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-1x4.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-2x1.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-2x2.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-2x3.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-2x4.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-3x1.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-3x2.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-3x3.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-3x4.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-4x1.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-4x2.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-4x3.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-4x4.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-1x1.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-1x2.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-1x3.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-1x4.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-2x1.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-2x2.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-2x3.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-2x4.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-3x1.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-3x2.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-3x3.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-3x4.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-4x1.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-4x2.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-4x3.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-4x4.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-1x1.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-1x2.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-1x3.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-1x4.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-2x1.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-2x2.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-2x3.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-2x4.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-3x1.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-3x2.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-3x3.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-3x4.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-4x1.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-4x2.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-4x3.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-4x4.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-1x1.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-1x2.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-1x3.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-1x4.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-2x1.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-2x2.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-2x3.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-2x4.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-3x1.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-3x2.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-3x3.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-3x4.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-4x1.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-4x2.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-4x3.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-4x4.label = Tray Weight
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.bambulab.ams-device.init.no-bridge = There is no bridge!
|
||||
thing-type.config.bambulab.ams-device.init.bridge-wrong-type = Bridge has wrong type!
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.bambulab.ams.label = Bambu Lab AMS Device
|
||||
thing-type.bambulab.ams.description = Represents a Bambu Lab AMS connected to a printer.
|
||||
thing-type.bambulab.ams.channel.ams-bed-temp-type-id-1.label = Bed Temperature Type
|
||||
thing-type.bambulab.ams.channel.ams-bed-temp-type-id-2.label = Bed Temperature Type
|
||||
thing-type.bambulab.ams.channel.ams-bed-temp-type-id-3.label = Bed Temperature Type
|
||||
thing-type.bambulab.ams.channel.ams-bed-temp-type-id-4.label = Bed Temperature Type
|
||||
thing-type.bambulab.ams.channel.ams-bed-temperature-id-1.label = Bed Temperature
|
||||
thing-type.bambulab.ams.channel.ams-bed-temperature-id-2.label = Bed Temperature
|
||||
thing-type.bambulab.ams.channel.ams-bed-temperature-id-3.label = Bed Temperature
|
||||
thing-type.bambulab.ams.channel.ams-bed-temperature-id-4.label = Bed Temperature
|
||||
thing-type.bambulab.ams.channel.ams-ctype-id-1.label = ctype
|
||||
thing-type.bambulab.ams.channel.ams-ctype-id-2.label = ctype
|
||||
thing-type.bambulab.ams.channel.ams-ctype-id-3.label = ctype
|
||||
thing-type.bambulab.ams.channel.ams-ctype-id-4.label = ctype
|
||||
thing-type.bambulab.ams.channel.ams-k-id-1.label = k
|
||||
thing-type.bambulab.ams.channel.ams-k-id-2.label = k
|
||||
thing-type.bambulab.ams.channel.ams-k-id-3.label = k
|
||||
thing-type.bambulab.ams.channel.ams-k-id-4.label = k
|
||||
thing-type.bambulab.ams.channel.ams-n-id-1.label = n
|
||||
thing-type.bambulab.ams.channel.ams-n-id-2.label = n
|
||||
thing-type.bambulab.ams.channel.ams-n-id-3.label = n
|
||||
thing-type.bambulab.ams.channel.ams-n-id-4.label = n
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-max-id-1.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-max-id-1.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-max-id-2.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-max-id-2.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-max-id-3.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-max-id-3.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-max-id-4.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-max-id-4.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-min-id-1.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-min-id-1.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-min-id-2.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-min-id-2.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-min-id-3.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-min-id-3.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-min-id-4.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.ams.channel.ams-nozzle-temperature-min-id-4.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.ams.channel.ams-remain-id-1.label = Remain
|
||||
thing-type.bambulab.ams.channel.ams-remain-id-1.description = Amount of remaining filament.
|
||||
thing-type.bambulab.ams.channel.ams-remain-id-2.label = Remain
|
||||
thing-type.bambulab.ams.channel.ams-remain-id-2.description = Amount of remaining filament.
|
||||
thing-type.bambulab.ams.channel.ams-remain-id-3.label = Remain
|
||||
thing-type.bambulab.ams.channel.ams-remain-id-3.description = Amount of remaining filament.
|
||||
thing-type.bambulab.ams.channel.ams-remain-id-4.label = Remain
|
||||
thing-type.bambulab.ams.channel.ams-remain-id-4.description = Amount of remaining filament.
|
||||
thing-type.bambulab.ams.channel.ams-tag-uuid-id-1.label = Tag UUID
|
||||
thing-type.bambulab.ams.channel.ams-tag-uuid-id-2.label = Tag UUID
|
||||
thing-type.bambulab.ams.channel.ams-tag-uuid-id-3.label = Tag UUID
|
||||
thing-type.bambulab.ams.channel.ams-tag-uuid-id-4.label = Tag UUID
|
||||
thing-type.bambulab.ams.channel.ams-tray-diameter-id-1.label = Tray Diameter
|
||||
thing-type.bambulab.ams.channel.ams-tray-diameter-id-2.label = Tray Diameter
|
||||
thing-type.bambulab.ams.channel.ams-tray-diameter-id-3.label = Tray Diameter
|
||||
thing-type.bambulab.ams.channel.ams-tray-diameter-id-4.label = Tray Diameter
|
||||
thing-type.bambulab.ams.channel.ams-tray-id-name-id-1.label = Tray ID Name
|
||||
thing-type.bambulab.ams.channel.ams-tray-id-name-id-2.label = Tray ID Name
|
||||
thing-type.bambulab.ams.channel.ams-tray-id-name-id-3.label = Tray ID Name
|
||||
thing-type.bambulab.ams.channel.ams-tray-id-name-id-4.label = Tray ID Name
|
||||
thing-type.bambulab.ams.channel.ams-tray-info-idx-id-1.label = Tray Info IDX Name
|
||||
thing-type.bambulab.ams.channel.ams-tray-info-idx-id-2.label = Tray Info IDX Name
|
||||
thing-type.bambulab.ams.channel.ams-tray-info-idx-id-3.label = Tray Info IDX Name
|
||||
thing-type.bambulab.ams.channel.ams-tray-info-idx-id-4.label = Tray Info IDX Name
|
||||
thing-type.bambulab.ams.channel.ams-tray-previous.label = Previously Loaded Tray
|
||||
thing-type.bambulab.ams.channel.ams-tray-sub-brands-id-1.label = Tray Sub Brands
|
||||
thing-type.bambulab.ams.channel.ams-tray-sub-brands-id-2.label = Tray Sub Brands
|
||||
thing-type.bambulab.ams.channel.ams-tray-sub-brands-id-3.label = Tray Sub Brands
|
||||
thing-type.bambulab.ams.channel.ams-tray-sub-brands-id-4.label = Tray Sub Brands
|
||||
thing-type.bambulab.ams.channel.ams-tray-temperature-id-1.label = Tray Temperature
|
||||
thing-type.bambulab.ams.channel.ams-tray-temperature-id-2.label = Tray Temperature
|
||||
thing-type.bambulab.ams.channel.ams-tray-temperature-id-3.label = Tray Temperature
|
||||
thing-type.bambulab.ams.channel.ams-tray-temperature-id-4.label = Tray Temperature
|
||||
thing-type.bambulab.ams.channel.ams-tray-time-id-1.label = Tray Time
|
||||
thing-type.bambulab.ams.channel.ams-tray-time-id-2.label = Tray Time
|
||||
thing-type.bambulab.ams.channel.ams-tray-time-id-3.label = Tray Time
|
||||
thing-type.bambulab.ams.channel.ams-tray-time-id-4.label = Tray Time
|
||||
thing-type.bambulab.ams.channel.ams-tray-weight-id-1.label = Tray Weight
|
||||
thing-type.bambulab.ams.channel.ams-tray-weight-id-2.label = Tray Weight
|
||||
thing-type.bambulab.ams.channel.ams-tray-weight-id-3.label = Tray Weight
|
||||
thing-type.bambulab.ams.channel.ams-tray-weight-id-4.label = Tray Weight
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.bambulab.ams.number.label = AMS Number
|
||||
thing-type.config.bambulab.ams.number.description = Order of the AMS
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.bambulab.printer.channel.speed-level.label = Print Speed Level
|
||||
thing-type.bambulab.printer.channel.speed-level.description = Current speed setting of the print job.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-0x0.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-0x1.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-0x2.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-0x3.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-1x0.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-2x0.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temp-type-id-3x0.label = Bed Temperature Type
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-0x0.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-0x1.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-0x2.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-0x3.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-1x0.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-2x0.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-bed-temperature-id-3x0.label = Bed Temperature
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-0x0.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-0x1.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-0x2.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-0x3.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-1x0.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-2x0.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-ctype-id-3x0.label = ctype
|
||||
thing-type.bambulab.printer.channel.ams-k-id-0x0.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-0x1.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-0x2.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-0x3.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-1x0.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-2x0.label = k
|
||||
thing-type.bambulab.printer.channel.ams-k-id-3x0.label = k
|
||||
thing-type.bambulab.printer.channel.ams-n-id-0x0.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-0x1.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-0x2.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-0x3.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-1x0.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-2x0.label = n
|
||||
thing-type.bambulab.printer.channel.ams-n-id-3x0.label = n
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-0x0.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-0x0.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-0x1.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-0x1.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-0x2.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-0x2.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-0x3.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-0x3.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-1x0.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-1x0.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-2x0.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-2x0.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-3x0.label = Nozzle Temperature Max
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-max-id-3x0.description = Maximum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-0x0.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-0x0.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-0x1.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-0x1.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-0x2.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-0x2.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-0x3.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-0x3.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-1x0.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-1x0.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-2x0.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-2x0.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-3x0.label = Nozzle Temperature Min
|
||||
thing-type.bambulab.printer.channel.ams-nozzle-temperature-min-id-3x0.description = Minimum nozzle temperature for tray.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-0x0.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-0x0.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-0x1.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-0x1.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-0x2.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-0x2.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-0x3.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-0x3.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-1x0.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-1x0.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-2x0.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-2x0.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-3x0.label = Remain
|
||||
thing-type.bambulab.printer.channel.ams-remain-id-3x0.description = Amount of remaining filament.
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-0x0.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-0x1.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-0x2.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-0x3.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-1x0.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-2x0.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tag-uuid-id-3x0.label = Tag UUID
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-0x0.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-0x1.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-0x2.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-0x3.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-1x0.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-2x0.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-diameter-id-3x0.label = Tray Diameter
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-0x0.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-0x1.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-0x2.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-0x3.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-1x0.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-2x0.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-id-name-id-3x0.label = Tray ID Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-0x0.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-0x1.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-0x2.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-0x3.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-1x0.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-2x0.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-info-idx-id-3x0.label = Tray Info IDX Name
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-0x0.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-0x1.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-0x2.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-0x3.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-1x0.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-2x0.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-sub-brands-id-3x0.label = Tray Sub Brands
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-0x0.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-0x1.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-0x2.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-0x3.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-1x0.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-2x0.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-temperature-id-3x0.label = Tray Temperature
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-0x0.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-0x1.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-0x2.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-0x3.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-1x0.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-2x0.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-time-id-3x0.label = Tray Time
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-0x0.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-0x1.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-0x2.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-0x3.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-1x0.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-2x0.label = Tray Weight
|
||||
thing-type.bambulab.printer.channel.ams-tray-weight-id-3x0.label = Tray Weight
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.bambulab.temperature.label = Temperature
|
||||
@@ -191,8 +913,6 @@ channel-type.bambulab.wifi-channel.description = Current WiFi signal strength.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.bambulab.printer.channel.command.label = Printer Command
|
||||
thing-type.bambulab.printer.channel.command.description = Current command being executed by the printer.
|
||||
thing-type.bambulab.printer.channel.message.label = Message Code
|
||||
thing-type.bambulab.printer.channel.message.description = Message code from the printer.
|
||||
thing-type.bambulab.printer.channel.sequenceId.label = Sequence ID
|
||||
@@ -240,4 +960,5 @@ printer.handler.init.noHostname = Please pass hostname!
|
||||
printer.handler.init.invalidHostname = Invalid hostname "{0}"!
|
||||
printer.handler.init.cannotConnectCamera = Cannot connect to camera! {0}
|
||||
printer.handler.init.noSerial = Please pass serial!
|
||||
printer.handler.init.connectionLost = Connection lost! {0}
|
||||
printer.handler.init.noAccessCode = Please pass access code!
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="bambulab"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="ams-device">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="printer"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Bambu Lab AMS Device</label>
|
||||
<description>Represents a Bambu Lab AMS connected to a printer.</description>
|
||||
|
||||
<channel-groups>
|
||||
<!-- each AMS contains exactly 4 trays -->
|
||||
<!-- not all of them might be used at the same time -->
|
||||
<!-- but still channels should exist for them -->
|
||||
<channel-group id="ams-tray-1" typeId="ams-tray">
|
||||
<label>AMS Tray #1</label>
|
||||
</channel-group>
|
||||
<channel-group id="ams-tray-2" typeId="ams-tray">
|
||||
<label>AMS Tray #2</label>
|
||||
</channel-group>
|
||||
<channel-group id="ams-tray-3" typeId="ams-tray">
|
||||
<label>AMS Tray #3</label>
|
||||
</channel-group>
|
||||
<channel-group id="ams-tray-4" typeId="ams-tray">
|
||||
<label>AMS Tray #4</label>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
|
||||
<representation-property>number</representation-property>
|
||||
<config-description>
|
||||
<parameter name="number" type="integer" required="true" min="1" max="4">
|
||||
<label>AMS Number</label>
|
||||
<description>Order of the AMS</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="bambulab"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
<channel-group-type id="ams-tray">
|
||||
<label>AMS Tray</label>
|
||||
<description>This channel group represents a single AMS tray.</description>
|
||||
<channels>
|
||||
<channel id="ams-tray-type" typeId="tray-type"/>
|
||||
<channel id="ams-tray-color" typeId="tray-color"/>
|
||||
<channel id="ams-nozzle-temperature-max" typeId="temperature-setpoint">
|
||||
<label>Nozzle Temperature Max</label>
|
||||
<description>Maximum nozzle temperature for tray.</description>
|
||||
</channel>
|
||||
<channel id="ams-nozzle-temperature-min" typeId="temperature-setpoint">
|
||||
<label>Nozzle Temperature Min</label>
|
||||
<description>Minimum nozzle temperature for tray.</description>
|
||||
</channel>
|
||||
<channel id="ams-remain" typeId="percent">
|
||||
<label>Remain</label>
|
||||
<description>Amount of remaining filament.</description>
|
||||
</channel>
|
||||
<channel id="ams-k" typeId="number-advanced">
|
||||
<label>k</label>
|
||||
</channel>
|
||||
<channel id="ams-n" typeId="number-advanced">
|
||||
<label>n</label>
|
||||
</channel>
|
||||
<channel id="ams-tag-uuid" typeId="string-advanced">
|
||||
<label>Tag UUID</label>
|
||||
</channel>
|
||||
<channel id="ams-tray-id-name" typeId="string-advanced">
|
||||
<label>Tray ID Name</label>
|
||||
</channel>
|
||||
<channel id="ams-tray-info-idx" typeId="string-advanced">
|
||||
<label>Tray Info IDX Name</label>
|
||||
</channel>
|
||||
<channel id="ams-tray-sub-brands" typeId="string-advanced">
|
||||
<label>Tray Sub Brands</label>
|
||||
</channel>
|
||||
<channel id="ams-tray-weight" typeId="number-advanced">
|
||||
<label>Tray Weight</label>
|
||||
</channel>
|
||||
<channel id="ams-tray-diameter" typeId="number-advanced">
|
||||
<label>Tray Diameter</label>
|
||||
</channel>
|
||||
<channel id="ams-tray-temperature" typeId="temperature-measurement-advanced">
|
||||
<label>Tray Temperature</label>
|
||||
</channel>
|
||||
<channel id="ams-tray-time" typeId="number-advanced">
|
||||
<label>Tray Time</label>
|
||||
</channel>
|
||||
<channel id="ams-bed-temp-type" typeId="string-advanced">
|
||||
<label>Bed Temperature Type</label>
|
||||
</channel>
|
||||
<channel id="ams-bed-temperature" typeId="temperature-measurement-advanced">
|
||||
<label>Bed Temperature</label>
|
||||
</channel>
|
||||
<channel id="ams-ctype" typeId="number-advanced">
|
||||
<label>ctype</label>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
</thing:thing-descriptions>
|
||||
+77
-15
@@ -11,7 +11,17 @@
|
||||
<tag>Measurement</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="temperature-measurement-advanced" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Current Temperature</label>
|
||||
<category>Temperature</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="temperature-setpoint">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
@@ -21,7 +31,7 @@
|
||||
<tag>Setpoint</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="string">
|
||||
<item-type>String</item-type>
|
||||
@@ -32,6 +42,11 @@
|
||||
<item-type>String</item-type>
|
||||
<label>RW String Channel</label>
|
||||
</channel-type>
|
||||
<channel-type id="string-advanced" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Advanced String Channel</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="wifi">
|
||||
<item-type unitHint="dBm">Number:Power</item-type>
|
||||
<label>Wi-Fi Signal Strength</label>
|
||||
@@ -43,10 +58,20 @@
|
||||
<label>Number Channel</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="time-min">
|
||||
<item-type unitHint="min">Number:Time</item-type>
|
||||
<label>Time (min)</label>
|
||||
<state readOnly="true" pattern="%1$tHh:%1$tMm"/>
|
||||
</channel-type>
|
||||
<channel-type id="number-advanced" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Advanced Number Channel</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="percent">
|
||||
<item-type unitHint="%">Number:Dimensionless</item-type>
|
||||
<label>Percent Channel</label>
|
||||
<state readOnly="true" min="0" max="100" pattern="%.2f %unit%"/>
|
||||
<state readOnly="true" min="0" max="100"/>
|
||||
</channel-type>
|
||||
<channel-type id="boolean">
|
||||
<item-type>Switch</item-type>
|
||||
@@ -86,21 +111,58 @@
|
||||
</command>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="on-off-command">
|
||||
<!-- tray -->
|
||||
<channel-type id="tray-type">
|
||||
<item-type>String</item-type>
|
||||
<label>ON/OFF Channel</label>
|
||||
<category>Lightbulb</category>
|
||||
<state>
|
||||
<label>Tray Type</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="ON">ON</option>
|
||||
<option value="OFF">OFF</option>
|
||||
<option value="PLA">PLA</option>
|
||||
<option value="PETG">PETG</option>
|
||||
<option value="ABS">ABS</option>
|
||||
<option value="TPU">TPU</option>
|
||||
<option value="ASA">ASA</option>
|
||||
<option value="PA">PA</option>
|
||||
<option value="PC">PC</option>
|
||||
<option value="PVA">PVA</option>
|
||||
<option value="HIPS">HIPS</option>
|
||||
</options>
|
||||
</state>
|
||||
<command>
|
||||
<options>
|
||||
<option value="ON">ON</option>
|
||||
<option value="OFF">OFF</option>
|
||||
</options>
|
||||
</command>
|
||||
</channel-type>
|
||||
<channel-type id="tray-color">
|
||||
<item-type>Color</item-type>
|
||||
<label>Tray Color</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="tray-loaded">
|
||||
<item-type>String</item-type>
|
||||
<label>Currently Loaded Tray</label>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="EMPTY">No tray loaded</option>
|
||||
<option value="VTRAY">External Spool</option>
|
||||
<!-- AMS 1 -->
|
||||
<option value="AMS_1_1">AMS #1 / Tray 1</option>
|
||||
<option value="AMS_1_2">AMS #1 / Tray 2</option>
|
||||
<option value="AMS_1_3">AMS #1 / Tray 3</option>
|
||||
<option value="AMS_1_4">AMS #1 / Tray 4</option>
|
||||
<!-- AMS 2 -->
|
||||
<option value="AMS_2_1">AMS #2 / Tray 1</option>
|
||||
<option value="AMS_2_2">AMS #2 / Tray 2</option>
|
||||
<option value="AMS_2_3">AMS #2 / Tray 3</option>
|
||||
<option value="AMS_2_4">AMS #2 / Tray 4</option>
|
||||
<!-- AMS 3 -->
|
||||
<option value="AMS_3_1">AMS #3 / Tray 1</option>
|
||||
<option value="AMS_3_2">AMS #3 / Tray 2</option>
|
||||
<option value="AMS_3_3">AMS #3 / Tray 3</option>
|
||||
<option value="AMS_3_4">AMS #3 / Tray 4</option>
|
||||
<!-- AMS 4 -->
|
||||
<option value="AMS_4_1">AMS #4 / Tray 1</option>
|
||||
<option value="AMS_4_2">AMS #4 / Tray 2</option>
|
||||
<option value="AMS_4_3">AMS #4 / Tray 3</option>
|
||||
<option value="AMS_4_4">AMS #4 / Tray 4</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
||||
+82
-5
@@ -4,11 +4,17 @@
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="printer">
|
||||
<bridge-type id="printer">
|
||||
<label>Bambu Lab Printer</label>
|
||||
<description>Represents a Bambu Lab 3D printer connected via MQTT</description>
|
||||
|
||||
<channels>
|
||||
<channel id="command" typeId="string-rw">
|
||||
<label>Command</label>
|
||||
<description><![CDATA[
|
||||
Send command to run. See <a href="https://github.com/openhab/openhab-addons/tree/main/bundles/org.openhab.binding.bambulab#sendcommand">Actions > sendCommand</a>
|
||||
]]></description>
|
||||
</channel>
|
||||
<channel id="nozzle-temperature" typeId="temperature-measurement">
|
||||
<label>Nozzle Temperature</label>
|
||||
<description>Current temperature of the nozzle.</description>
|
||||
@@ -37,7 +43,7 @@
|
||||
<label>Print Progress</label>
|
||||
<description>Percentage of the print completed.</description>
|
||||
</channel>
|
||||
<channel id="mc-remaining-time" typeId="number">
|
||||
<channel id="mc-remaining-time" typeId="time-min">
|
||||
<label>Remaining Print Time</label>
|
||||
<description>Estimated time remaining for the print in seconds.</description>
|
||||
</channel>
|
||||
@@ -105,14 +111,73 @@
|
||||
<channel id="camera-image" typeId="camera-image"/>
|
||||
|
||||
<!-- command channels -->
|
||||
<channel id="led-chamber" typeId="on-off-command">
|
||||
<channel id="led-chamber" typeId="boolean-rw">
|
||||
<label>Chamber LED</label>
|
||||
<description>Controls the LED lighting inside the printer chamber.</description>
|
||||
</channel>
|
||||
<channel id="led-work" typeId="on-off-command">
|
||||
<channel id="led-work" typeId="boolean-rw">
|
||||
<label>Work Area LED</label>
|
||||
<description>Controls the LED lighting for the work area.</description>
|
||||
</channel>
|
||||
|
||||
<!-- 🟩 AMS -->
|
||||
<channel id="ams-tray-now" typeId="tray-loaded"/>
|
||||
<channel id="ams-tray-previous" typeId="tray-loaded">
|
||||
<label>Previously Loaded Tray</label>
|
||||
</channel>
|
||||
<!-- 🟥 AMS -->
|
||||
<!-- 🟩 VTray -->
|
||||
<channel id="vtray-tray-type" typeId="tray-type"/>
|
||||
<channel id="vtray-tray-color" typeId="tray-color"/>
|
||||
<channel id="vtray-nozzle-temperature-max" typeId="temperature-setpoint">
|
||||
<label>Nozzle Temperature Max</label>
|
||||
<description>Maximum nozzle temperature for tray.</description>
|
||||
</channel>
|
||||
<channel id="vtray-nozzle-temperature-min" typeId="temperature-setpoint">
|
||||
<label>Nozzle Temperature Min</label>
|
||||
<description>Minimum nozzle temperature for tray.</description>
|
||||
</channel>
|
||||
<channel id="vtray-remain" typeId="percent">
|
||||
<label>Remain</label>
|
||||
<description>Amount of remaining filament.</description>
|
||||
</channel>
|
||||
<channel id="vtray-k" typeId="number-advanced">
|
||||
<label>k</label>
|
||||
</channel>
|
||||
<channel id="vtray-n" typeId="number-advanced">
|
||||
<label>n</label>
|
||||
</channel>
|
||||
<channel id="vtray-tag-uuid" typeId="string-advanced">
|
||||
<label>Tag UUID</label>
|
||||
</channel>
|
||||
<channel id="vtray-tray-id-name" typeId="string-advanced">
|
||||
<label>Tray ID Name</label>
|
||||
</channel>
|
||||
<channel id="vtray-tray-info-idx" typeId="string-advanced">
|
||||
<label>Tray Info IDX Name</label>
|
||||
</channel>
|
||||
<channel id="vtray-tray-sub-brands" typeId="string-advanced">
|
||||
<label>Tray Sub Brands</label>
|
||||
</channel>
|
||||
<channel id="vtray-tray-weight" typeId="number-advanced">
|
||||
<label>Tray Weight</label>
|
||||
</channel>
|
||||
<channel id="vtray-tray-diameter" typeId="number-advanced">
|
||||
<label>Tray Diameter</label>
|
||||
</channel>
|
||||
<channel id="vtray-tray-temperature" typeId="temperature-measurement-advanced">
|
||||
<label>Tray Temperature</label>
|
||||
</channel>
|
||||
<channel id="vtray-tray-time" typeId="number-advanced">
|
||||
<label>Tray Time</label>
|
||||
</channel>
|
||||
<channel id="vtray-bed-temp-type" typeId="string-advanced">
|
||||
<label>Bed Temperature Type</label>
|
||||
</channel>
|
||||
<channel id="vtray-bed-temperature" typeId="temperature-measurement-advanced">
|
||||
<label>Bed Temperature</label>
|
||||
</channel>
|
||||
<!-- 🟥 VTray -->
|
||||
</channels>
|
||||
|
||||
<representation-property>serial</representation-property>
|
||||
@@ -157,6 +222,18 @@
|
||||
<description>`bblp` for local mode or your Bambu Lab user (starts with `u_`).</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="reconnectTime" type="text" min="1">
|
||||
<label>Reconnect Time</label>
|
||||
<description>Time in seconds to wait before reconnect.</description>
|
||||
<default>300</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="reconnectMax" type="text" min="0">
|
||||
<label>Max Reconnections</label>
|
||||
<description>Max times to try reconnections.</description>
|
||||
<default>5</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<!-- 🟥Common -->
|
||||
<!-- 🟩MQTT -->
|
||||
<parameter name="scheme" type="text" required="false" groupName="mqtt">
|
||||
@@ -187,6 +264,6 @@
|
||||
</parameter>
|
||||
<!-- 🟥CAMERA -->
|
||||
</config-description>
|
||||
</thing-type>
|
||||
</bridge-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
||||
+52
-50
@@ -12,9 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.LedControlCommand.LedMode.*;
|
||||
import static pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.LedControlCommand.LedNode.*;
|
||||
|
||||
@@ -24,45 +22,20 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.assertj.core.api.ThrowableAssert;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.AmsControlCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.AmsFilamentSettingCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.AmsUserSettingCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.ChangeFilamentCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.Command;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.InfoCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.IpCamRecordCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.LedControlCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PrintSpeedCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PushingCommand;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.SystemCommand;
|
||||
|
||||
/**
|
||||
* @author Martin Grzeslowski - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PrinterActionsTest {
|
||||
PrinterActions printerActions = new PrinterActions();
|
||||
@Mock
|
||||
@Nullable
|
||||
PrinterHandler printerHandler;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
printerActions.setThingHandler(requireNonNull(printerHandler));
|
||||
}
|
||||
class CommandParserTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("should correctly parse GCodeLineCommand")
|
||||
@@ -77,10 +50,10 @@ class PrinterActionsTest {
|
||||
M104 S200 ; Set hotend temperature to 200°C""";
|
||||
|
||||
// when
|
||||
printerActions.sendCommand(command);
|
||||
var parsedCommand = CommandParser.parseCommand(command);
|
||||
|
||||
// then
|
||||
verify(requireNonNull(printerHandler)).sendCommand(new PrinterClient.Channel.GCodeLineCommand(
|
||||
assertThat(parsedCommand).isEqualTo(new PrinterClient.Channel.GCodeLineCommand(
|
||||
List.of("G28 ; Home all axes", "G90 ; Set to absolute positioning",
|
||||
"G1 X50 Y50 Z10 F1500 ; Move to position (50,50,10) at 1500 mm/min",
|
||||
"G1 X100 Y100 Z20 F2000 ; Move to position (100,100,20) at 2000 mm/min",
|
||||
@@ -95,7 +68,7 @@ class PrinterActionsTest {
|
||||
var command = "GCodeLine:123";
|
||||
|
||||
// when
|
||||
ThrowableAssert.ThrowingCallable when = () -> printerActions.sendCommand(command);
|
||||
ThrowableAssert.ThrowingCallable when = () -> CommandParser.parseCommand(command);
|
||||
|
||||
// then
|
||||
assertThatThrownBy(when)//
|
||||
@@ -105,29 +78,30 @@ class PrinterActionsTest {
|
||||
|
||||
@ParameterizedTest(name = "{index}: should {0}")
|
||||
@MethodSource
|
||||
void shouldRunCommand(String command, Command expectedCommand) {
|
||||
void shouldRunCommand(String command, PrinterClient.Channel.Command expectedCommand) {
|
||||
// when
|
||||
printerActions.sendCommand(command);
|
||||
var parsedCommand = CommandParser.parseCommand(command);
|
||||
|
||||
// then
|
||||
verify(requireNonNull(printerHandler)).sendCommand(expectedCommand);
|
||||
assertThat(parsedCommand).isEqualTo(expectedCommand);
|
||||
}
|
||||
|
||||
static Stream<Arguments> shouldRunCommand() {
|
||||
var infoCommandStream = Arrays.stream(InfoCommand.values())//
|
||||
var infoCommandStream = Arrays.stream(PrinterClient.Channel.InfoCommand.values())//
|
||||
.map(value -> Arguments.of("Info:" + value.name(), value));
|
||||
var pushingCommandStream = stream(Arguments.of("Pushing:11:22", new PushingCommand(11, 22)));
|
||||
var pushingCommandStream = stream(
|
||||
Arguments.of("Pushing:11:22", new PrinterClient.Channel.PushingCommand(11, 22)));
|
||||
var printCommandStream = Arrays.stream(PrinterClient.Channel.PrintCommand.values())//
|
||||
.map(value -> Arguments.of("Print:" + value.name(), value));
|
||||
var changeFilamentCommandStream = stream(
|
||||
Arguments.of("ChangeFilament:11:22:33", new ChangeFilamentCommand(11, 22, 33)));
|
||||
var amsUserSettingCommandStream = stream(
|
||||
Arguments.of("AmsUserSetting:11:tRuE:FaLsE", new AmsUserSettingCommand(11, true, false)));
|
||||
Arguments.of("ChangeFilament:11:22:33", new PrinterClient.Channel.ChangeFilamentCommand(11, 22, 33)));
|
||||
var amsUserSettingCommandStream = stream(Arguments.of("AmsUserSetting:11:tRuE:FaLsE",
|
||||
new PrinterClient.Channel.AmsUserSettingCommand(11, true, false)));
|
||||
var amsFilamentSettingCommandStream = stream(Arguments.of("AmsFilamentSetting:11:22:s3:s4:55:66:s7",
|
||||
new AmsFilamentSettingCommand(11, 22, "s3", "s4", 55, 66, "s7")));
|
||||
var amsControlCommandStream = Arrays.stream(AmsControlCommand.values())//
|
||||
new PrinterClient.Channel.AmsFilamentSettingCommand(11, 22, "s3", "s4", 55, 66, "s7")));
|
||||
var amsControlCommandStream = Arrays.stream(PrinterClient.Channel.AmsControlCommand.values())//
|
||||
.map(value -> Arguments.of("AmsControl:" + value.name(), value));
|
||||
var printSpeedCommandStream = Arrays.stream(PrintSpeedCommand.values())//
|
||||
var printSpeedCommandStream = Arrays.stream(PrinterClient.Channel.PrintSpeedCommand.values())//
|
||||
.map(value -> Arguments.of("PrintSpeed:" + value.name(), value));
|
||||
var gCodeFileCommandStream = stream(
|
||||
Arguments.of("GCodeFile:s1", new PrinterClient.Channel.GCodeFileCommand("s1")));
|
||||
@@ -138,17 +112,17 @@ class PrinterActionsTest {
|
||||
l3""", new PrinterClient.Channel.GCodeLineCommand(List.of("l1", "l2", "l3"), "s1")));
|
||||
var ledControlCommandStream = stream(//
|
||||
Arguments.of("LedControl:CHAMBER_LIGHT:ON",
|
||||
new LedControlCommand(CHAMBER_LIGHT, ON, null, null, null, null)), //
|
||||
new PrinterClient.Channel.LedControlCommand(CHAMBER_LIGHT, ON, null, null, null, null)), //
|
||||
Arguments.of("LedControl:WORK_LIGHT:OFF",
|
||||
new LedControlCommand(WORK_LIGHT, OFF, null, null, null, null)), //
|
||||
new PrinterClient.Channel.LedControlCommand(WORK_LIGHT, OFF, null, null, null, null)), //
|
||||
Arguments.of("LedControl:CHAMBER_LIGHT:FLASHING:11:22:33:44",
|
||||
new LedControlCommand(CHAMBER_LIGHT, FLASHING, 11, 22, 33, 44))//
|
||||
new PrinterClient.Channel.LedControlCommand(CHAMBER_LIGHT, FLASHING, 11, 22, 33, 44))//
|
||||
);
|
||||
var systemCommandStream = Arrays.stream(SystemCommand.values())//
|
||||
var systemCommandStream = Arrays.stream(PrinterClient.Channel.SystemCommand.values())//
|
||||
.map(value -> Arguments.of("System:" + value.name(), value));
|
||||
var ipCamRecordCommandStream = stream(//
|
||||
Arguments.of("IpCamRecord:tRue", new IpCamRecordCommand(true)), //
|
||||
Arguments.of("IpCamRecord:fAlSe", new IpCamRecordCommand(false))//
|
||||
Arguments.of("IpCamRecord:tRue", new PrinterClient.Channel.IpCamRecordCommand(true)), //
|
||||
Arguments.of("IpCamRecord:fAlSe", new PrinterClient.Channel.IpCamRecordCommand(false))//
|
||||
);
|
||||
var ipCamTimelapsCommandStream = stream(//
|
||||
Arguments.of("IpCamTimelaps:tRue", new PrinterClient.Channel.IpCamTimelapsCommand(true)), //
|
||||
@@ -173,4 +147,32 @@ class PrinterActionsTest {
|
||||
static <T> Stream<T> stream(T... values) {
|
||||
return Stream.of(values);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{index}: should support command {0}")
|
||||
@MethodSource
|
||||
void shouldSupportCommand(String command) {
|
||||
// when
|
||||
ThrowableAssert.ThrowingCallable when = () -> CommandParser.parseCommand(command);
|
||||
|
||||
// then
|
||||
assertThatThrownBy(when)//
|
||||
.message()//
|
||||
// if an error message starts with "Unknown..." it means that the giant if statement did not cover
|
||||
// command.
|
||||
//
|
||||
// it might happen if JBambuApi library adds a new subclass of Command and addon developers would not
|
||||
// add it to PrinterActions.
|
||||
//
|
||||
// it might happen that there will be some other IllegalArgumentException (like not enough params) would
|
||||
// be thrown, but none of them starts with "Unknown...".
|
||||
.doesNotStartWith("Unknown command name: " + command);
|
||||
}
|
||||
|
||||
static Stream<Arguments> shouldSupportCommand() {
|
||||
return new Reflections(PrinterClient.Channel.Command.class)//
|
||||
.getSubTypesOf(PrinterClient.Channel.Command.class)//
|
||||
.stream()//
|
||||
.map(Class::getSimpleName)//
|
||||
.map(command -> command.replace("Command", "")).map(Arguments::of);
|
||||
}
|
||||
}
|
||||
+7
-5
@@ -34,10 +34,11 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.openhab.binding.bambulab.internal.BambuLabBindingConstants.Channel;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.Command;
|
||||
import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PrintSpeedCommand;
|
||||
|
||||
/**
|
||||
@@ -47,16 +48,16 @@ import pl.grzeslowski.jbambuapi.mqtt.PrinterClient.Channel.PrintSpeedCommand;
|
||||
@NonNullByDefault
|
||||
class PrinterHandlerTest {
|
||||
@Spy
|
||||
PrinterHandler printerHandler = new PrinterHandler(mock(Thing.class));
|
||||
PrinterHandler printerHandler = new PrinterHandler(mock(Bridge.class));
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
lenient().doNothing().when(printerHandler).sendCommand(any(PrinterClient.Channel.Command.class));
|
||||
lenient().doNothing().when(printerHandler).sendCommand(any(Command.class));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "Should handle {0} command for {1} channel and send {2}")
|
||||
@MethodSource
|
||||
public void testSendLightCommand(OnOffType command, Channel channel, PrinterClient.Channel.Command sendCommand) {
|
||||
public void testSendLightCommand(OnOffType command, Channel channel, Command sendCommand) {
|
||||
// Given
|
||||
var channelUID = new ChannelUID("bambulab:printer:test:" + channel);
|
||||
|
||||
@@ -116,7 +117,8 @@ class PrinterHandlerTest {
|
||||
printerHandler.handleCommand(channelUID, OnOffType.ON);
|
||||
|
||||
// Then
|
||||
verify(printerHandler, never()).sendCommand(any());
|
||||
verify(printerHandler, never()).sendCommand(any(Command.class));
|
||||
verify(printerHandler, never()).sendCommand(anyString());
|
||||
}
|
||||
|
||||
static Stream<Arguments> notImplementedCommands() {
|
||||
|
||||
+522
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
import static org.openhab.core.library.unit.Units.DECIBEL_MILLIWATTS;
|
||||
import static org.openhab.core.types.UnDefType.UNDEF;
|
||||
import static tech.units.indriya.unit.Units.PERCENT;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
|
||||
/**
|
||||
* @author Martin Grześlowski - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class StateParserHelperTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a valid temperature string, when parseTemperatureType is called, then it returns QuantityType with CELSIUS unit")
|
||||
void testParseTemperatureTypeConvertsValidStringToQuantityType() {
|
||||
// Given
|
||||
var temperatureString = "23.5";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseTemperatureType(temperatureString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
assertThat(result.get()).isInstanceOf(QuantityType.class);
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(23.5);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(CELSIUS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a valid Number, when parseDecimalType is called, then it returns DecimalType")
|
||||
void testParseDecimalTypeConvertsValidNumberToDecimalType() {
|
||||
// Given
|
||||
var number = 42.5;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseDecimalType(number);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
assertThat(result.get()).isInstanceOf(DecimalType.class);
|
||||
assertThat(((DecimalType) result.get()).doubleValue()).isEqualTo(42.5);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a valid numeric string, when parseDecimalType is called, then it returns DecimalType")
|
||||
void testParseDecimalTypeConvertsValidStringToDecimalType() {
|
||||
// Given
|
||||
var decimalString = "123.45";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseDecimalType(decimalString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
assertThat(result.get()).isInstanceOf(DecimalType.class);
|
||||
assertThat(((DecimalType) result.get()).doubleValue()).isEqualTo(123.45);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a non-null string, when parseStringType is called, then it returns StringType")
|
||||
void testParseStringTypeConvertsNonNullStringToStringType() {
|
||||
// Given
|
||||
var inputString = "test string";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseStringType(inputString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
assertThat(result.get()).isInstanceOf(StringType.class);
|
||||
assertThat(result.get().toString()).isEqualTo("test string");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a valid dBm pattern string, when parseWifiChannel is called, then it returns QuantityType with DECIBEL_MILLIWATTS unit")
|
||||
void testParseWifiChannelConvertsValidDbmPatternToQuantityType() {
|
||||
// Given
|
||||
var wifiString = "-75dBm";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseWifiChannel(wifiString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isInstanceOf(QuantityType.class);
|
||||
var quantityType = (QuantityType<?>) result;
|
||||
assertThat(quantityType.intValue()).isEqualTo(-75);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(DECIBEL_MILLIWATTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a valid 6-character hex string, when parseColor is called, then it returns HSB color State")
|
||||
void testParseColorConvertsValidHexStringToHsbColor() {
|
||||
// Given
|
||||
var colorHex = "FF0000"; // Red in RGB
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseColor(colorHex);
|
||||
|
||||
// Then
|
||||
assertThat(result).isNotEqualTo(UNDEF);
|
||||
|
||||
// HSB for pure red should have H=0, S=100, B=100
|
||||
var hsbValues = result.toString().split(",");
|
||||
assertThat(Double.parseDouble(hsbValues[0])).isCloseTo(0.0, within(0.1));
|
||||
assertThat(Double.parseDouble(hsbValues[1])).isCloseTo(100.0, within(0.1));
|
||||
assertThat(Double.parseDouble(hsbValues[2])).isCloseTo(100.0, within(0.1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a null input, when parseTemperatureType is called, then it returns empty Optional")
|
||||
void testParseTemperatureTypeReturnsEmptyOptionalForNullInput() {
|
||||
// Given
|
||||
String temperatureString = null;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseTemperatureType(temperatureString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a non-numeric string, when parseTemperatureType is called, then it returns empty Optional")
|
||||
void testParseTemperatureTypeReturnsEmptyOptionalForNonNumericString() {
|
||||
// Given
|
||||
var temperatureString = "not-a-number";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseTemperatureType(temperatureString);
|
||||
|
||||
// Then
|
||||
assertThat(result).contains(UNDEF);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a null Number, when parseDecimalType is called, then it returns empty Optional")
|
||||
void testParseDecimalTypeReturnsEmptyOptionalForNullNumber() {
|
||||
// Given
|
||||
Number number = null;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseDecimalType(number);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a null String, when parseDecimalType is called, then it returns empty Optional")
|
||||
void testParseDecimalTypeReturnsEmptyOptionalForNullString() {
|
||||
// Given
|
||||
String decimalString = null;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseDecimalType(decimalString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a non-numeric String, when parseDecimalType is called, then it returns empty Optional")
|
||||
void testParseDecimalTypeReturnsEmptyOptionalForNonNumericString() {
|
||||
// Given
|
||||
var decimalString = "not-a-decimal";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseDecimalType(decimalString);
|
||||
|
||||
// Then
|
||||
assertThat(result).contains(UNDEF);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a null String, when parseStringType is called, then it returns empty Optional")
|
||||
void testParseStringTypeReturnsEmptyOptionalForNullString() {
|
||||
// Given
|
||||
String inputString = null;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parseStringType(inputString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a valid numeric value, when parsePercentType is called, then it returns Optional with QuantityType")
|
||||
public void testValidNumericReturnsQuantityTypeWithPercentUnit() {
|
||||
// Given
|
||||
var numericValue = 75;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(numericValue);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
assertThat(result.get()).isInstanceOf(QuantityType.class);
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(75.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given an integer value, when parsePercentType is called, then it returns correct QuantityType")
|
||||
public void testIntegerValuesAreCorrectlyParsed() {
|
||||
// Given
|
||||
var integerValue = 100;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(integerValue);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(100.0);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a decimal value, when parsePercentType is called, then it returns correct QuantityType")
|
||||
public void testDecimalValuesAreCorrectlyParsed() {
|
||||
// Given
|
||||
var decimalValue = 50.5;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(decimalValue);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(50.5);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a zero value, when parsePercentType is called, then it returns QuantityType with zero value")
|
||||
public void testZeroValueIsCorrectlyParsed2() {
|
||||
// Given
|
||||
var zeroValue = 0;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(zeroValue);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isZero();
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a negative value, when parsePercentType is called, then it returns QuantityType with negative value")
|
||||
public void testNegativeValuesAreCorrectlyParsed2() {
|
||||
// Given
|
||||
var negativeValue = -10;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(negativeValue);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(-10.0);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a null input, when parsePercentType is called, then it returns Optional.empty")
|
||||
public void testNullInputReturnsEmptyOptional2() {
|
||||
// Given
|
||||
Number nullValue = null;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(nullValue);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given an empty string input, when parsePercentType is called, then it returns Optional.empty")
|
||||
public void testEmptyStringInputReturnsEmptyOptional2() {
|
||||
// This test is not applicable as the method only accepts Number objects
|
||||
// The method signature is: parsePercentType(@Nullable Number percent)
|
||||
// Empty strings would be handled by a different method before this one is called
|
||||
|
||||
// For completeness, we'll verify that null returns empty
|
||||
// Given
|
||||
Number nullValue = null;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(nullValue);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a non-numeric string input, when parsePercentType is called, then it returns Optional.empty")
|
||||
public void testNonNumericStringReturnsEmptyOptional2() {
|
||||
// This test is not applicable as the method only accepts Number objects
|
||||
// The method signature is: parsePercentType(@Nullable Number percent)
|
||||
// Non-numeric strings would be handled by a different method before this one is called
|
||||
|
||||
// For completeness, we'll verify that null returns empty
|
||||
// Given
|
||||
Number nullValue = null;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(nullValue);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a very large number, when parsePercentType is called, then it returns correct QuantityType")
|
||||
public void testVeryLargeNumbersAreHandledCorrectly2() {
|
||||
// Given
|
||||
var largeValue = Double.MAX_VALUE / 2;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(largeValue);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(largeValue);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Given a very small number, when parsePercentType is called, then it returns correct QuantityType")
|
||||
public void testVerySmallNumbersAreHandledCorrectly2() {
|
||||
// Given
|
||||
var smallValue = Double.MIN_VALUE;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(smallValue);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(smallValue);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Valid numeric string returns Optional containing QuantityType with PERCENT unit")
|
||||
public void testValidNumericStringReturnsQuantityTypeWithPercentUnit() {
|
||||
// Given
|
||||
var percentString = "75";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(percentString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
assertThat(result.get()).isInstanceOf(QuantityType.class);
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(75.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Integer string values like '100' are correctly parsed")
|
||||
public void testIntegerStringValuesAreCorrectlyParsed() {
|
||||
// Given
|
||||
var percentString = "100";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(percentString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(100.0);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Decimal string values like '50.5' are correctly parsed")
|
||||
public void testDecimalStringValuesAreCorrectlyParsed() {
|
||||
// Given
|
||||
var percentString = "50.5";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(percentString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(50.5);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Zero value '0' is correctly parsed")
|
||||
public void testZeroValueIsCorrectlyParsed() {
|
||||
// Given
|
||||
var percentString = "0";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(percentString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(0.0);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Negative values like '-10' are correctly parsed")
|
||||
public void testNegativeValuesAreCorrectlyParsed() {
|
||||
// Given
|
||||
var percentString = "-10";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(percentString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(-10.0);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Null input returns Optional.empty()")
|
||||
public void testNullInputReturnsEmptyOptional() {
|
||||
// Given
|
||||
String percentString = null;
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(percentString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Empty string input throws NumberFormatException and returns Optional.empty()")
|
||||
public void testEmptyStringInputReturnsEmptyOptional() {
|
||||
// Given
|
||||
var percentString = "";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(percentString);
|
||||
|
||||
// Then
|
||||
assertThat(result).contains(UNDEF);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Non-numeric string like 'abc' throws NumberFormatException and returns Optional.empty()")
|
||||
public void testNonNumericStringReturnsEmptyOptional() {
|
||||
// Given
|
||||
var percentString = "abc";
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(percentString);
|
||||
|
||||
// Then
|
||||
assertThat(result).contains(UNDEF);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Very large numbers near Double.MAX_VALUE are handled correctly")
|
||||
public void testVeryLargeNumbersAreHandledCorrectly() {
|
||||
// Given
|
||||
var percentString = String.valueOf(Double.MAX_VALUE);
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(percentString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(Double.MAX_VALUE);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Very small numbers near Double.MIN_VALUE are handled correctly")
|
||||
public void testVerySmallNumbersAreHandledCorrectly() {
|
||||
// Given
|
||||
var percentString = String.valueOf(Double.MIN_VALUE);
|
||||
|
||||
// When
|
||||
var result = StateParserHelper.parsePercentType(percentString);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
var quantityType = (QuantityType<?>) result.get();
|
||||
assertThat(quantityType.doubleValue()).isEqualTo(Double.MIN_VALUE);
|
||||
assertThat(quantityType.getUnit()).isEqualTo(PERCENT);
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.openhab.core.types.UnDefType.UNDEF;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
|
||||
/**
|
||||
* @author Martin Grześlowski - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class TrayHelperTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("String '255' returns EMPTY state")
|
||||
public void testString255ReturnsEmpty() {
|
||||
// Given
|
||||
var tray = "255";
|
||||
|
||||
// When
|
||||
var result = TrayHelper.updateTrayLoaded(tray);
|
||||
|
||||
// Then
|
||||
assertThat(result).isInstanceOf(StringType.class);
|
||||
assertThat(result.toString()).isEqualTo("EMPTY");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("String '254' returns VTRAY state")
|
||||
public void testString254ReturnsVtray() {
|
||||
// Given
|
||||
var tray = "254";
|
||||
|
||||
// When
|
||||
var result = TrayHelper.updateTrayLoaded(tray);
|
||||
|
||||
// Then
|
||||
assertThat(result).isInstanceOf(StringType.class);
|
||||
assertThat(result.toString()).isEqualTo("VTRAY");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{index}: should parse {0} as {1}")
|
||||
@MethodSource
|
||||
public void happyPath(int tray, String expected) {
|
||||
// When
|
||||
var result = TrayHelper.updateTrayLoaded(tray + "");
|
||||
|
||||
// Then
|
||||
assertThat(result.toString()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
static Stream<Arguments> happyPath() {
|
||||
return Stream.of(
|
||||
// AMS 1
|
||||
Arguments.of(0, "AMS_1_1"), //
|
||||
Arguments.of(1, "AMS_1_2"), //
|
||||
Arguments.of(2, "AMS_1_3"), //
|
||||
Arguments.of(3, "AMS_1_4"), //
|
||||
// AMS 2
|
||||
Arguments.of(0 + 4, "AMS_2_1"), //
|
||||
Arguments.of(1 + 4, "AMS_2_2"), //
|
||||
Arguments.of(2 + 4, "AMS_2_3"), //
|
||||
Arguments.of(3 + 4, "AMS_2_4"), //
|
||||
// AMS 3
|
||||
Arguments.of(0 + 8, "AMS_3_1"), //
|
||||
Arguments.of(1 + 8, "AMS_3_2"), //
|
||||
Arguments.of(2 + 8, "AMS_3_3"), //
|
||||
Arguments.of(3 + 8, "AMS_3_4"), //
|
||||
// AMS 4
|
||||
Arguments.of(0 + 12, "AMS_4_1"), //
|
||||
Arguments.of(1 + 12, "AMS_4_2"), //
|
||||
Arguments.of(2 + 12, "AMS_4_3"), //
|
||||
Arguments.of(3 + 12, "AMS_4_4"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Null input returns UNDEF")
|
||||
public void testNullInputReturnsUndef() {
|
||||
// Given
|
||||
String tray = null;
|
||||
|
||||
// When
|
||||
var result = TrayHelper.updateTrayLoaded(tray);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEqualTo(UNDEF);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Non-numeric string returns UNDEF")
|
||||
public void testNonNumericStringReturnsUndef() {
|
||||
// Given
|
||||
var tray = "not-a-number";
|
||||
|
||||
// When
|
||||
var result = TrayHelper.updateTrayLoaded(tray);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEqualTo(UNDEF);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Empty string returns UNDEF")
|
||||
public void testEmptyStringReturnsUndef() {
|
||||
// Given
|
||||
var tray = "";
|
||||
|
||||
// When
|
||||
var result = TrayHelper.updateTrayLoaded(tray);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEqualTo(UNDEF);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Negative numbers are processed as undef")
|
||||
public void negatives() {
|
||||
// Given
|
||||
var tray = "-5";
|
||||
|
||||
// When
|
||||
var result = TrayHelper.updateTrayLoaded(tray);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEqualTo(UNDEF);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Exceeding maximum tray value returns UNDEF with warning")
|
||||
public void testExceedingMaxValueReturnsUndefWithWarning() {
|
||||
// Given
|
||||
var tray = (TrayHelper.MAX_TRAY_VALUE + 1) + "";
|
||||
|
||||
// When
|
||||
var result = TrayHelper.updateTrayLoaded(tray);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEqualTo(UNDEF);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Value for `MAX_TRAY_VALUE` should return `AMS_4_4`")
|
||||
public void testForMaxTray() {
|
||||
// Given
|
||||
var tray = TrayHelper.MAX_TRAY_VALUE + "";
|
||||
|
||||
// When
|
||||
var result = TrayHelper.updateTrayLoaded(tray);
|
||||
|
||||
// Then
|
||||
assertThat(result.toString()).isEqualTo("AMS_4_4");
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bambulab.internal;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.binding.bambulab.internal.BambuLabBindingConstants.AmsChannel.TrayType;
|
||||
|
||||
/**
|
||||
* @author Martin Grześlowski - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class TrayTypeTest {
|
||||
@Test
|
||||
@DisplayName("Should find ABS when lowercase 'abs' is passed")
|
||||
public void testSuccessfullyFindsAbsWhenLowercaseAbsIsPassed() {
|
||||
// Given
|
||||
var input = "abs";
|
||||
|
||||
// When
|
||||
var result = TrayType.findTrayType(input);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
assertThat(result.get()).isEqualTo(TrayType.ABS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should find TPU when uppercase 'TPU' is passed")
|
||||
public void testSuccessfullyFindsTpuWhenUppercaseTpuIsPassed() {
|
||||
// Given
|
||||
var input = "TPU";
|
||||
|
||||
// When
|
||||
var result = TrayType.findTrayType(input);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
assertThat(result.get()).isEqualTo(TrayType.TPU);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should return empty Optional when input doesn't match any TrayType")
|
||||
public void testReturnsEmptyOptionalWhenInputDoesntMatchAnyTrayType() {
|
||||
// Given
|
||||
var input = "UNKNOWN_MATERIAL";
|
||||
|
||||
// When
|
||||
var result = TrayType.findTrayType(input);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle empty string input")
|
||||
public void testHandlesEmptyStringInput() {
|
||||
// Given
|
||||
var input = "";
|
||||
|
||||
// When
|
||||
var result = TrayType.findTrayType(input);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle mixed case inputs like 'PeTg' correctly")
|
||||
public void testHandlesMixedCaseInputsCorrectly() {
|
||||
// Given
|
||||
var input = "PeTg";
|
||||
|
||||
// When
|
||||
var result = TrayType.findTrayType(input);
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
assertThat(result.get()).isEqualTo(TrayType.PETG);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should handle inputs with leading/trailing whitespace")
|
||||
public void testHandlesInputsWithLeadingAndTrailingWhitespace() {
|
||||
// Given
|
||||
var input = " ABS ";
|
||||
|
||||
// When
|
||||
var result = TrayType.findTrayType(input);
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{index}: should parse {0} to {1}")
|
||||
@MethodSource
|
||||
void testPerformsCaseInsensitiveComparisonForAllEnumValues(String given, TrayType trayType) {
|
||||
assertThat(TrayType.findTrayType(given))//
|
||||
.isPresent()//
|
||||
.hasValueSatisfying(type -> assertThat(type).isEqualTo(trayType));
|
||||
}
|
||||
|
||||
static Stream<Arguments> testPerformsCaseInsensitiveComparisonForAllEnumValues() {
|
||||
return Arrays.stream(TrayType.values())//
|
||||
.flatMap(trayType -> Stream.of(//
|
||||
Arguments.of(trayType.name().toUpperCase(), trayType), //
|
||||
Arguments.of(trayType.name().toLowerCase(), trayType), //
|
||||
Arguments.of(trayType.name().charAt(0) + trayType.name().substring(1).toLowerCase(),
|
||||
trayType)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user