From 446be35ac51e3da4dac2c88d69cc3ff273a180be Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Mon, 28 Nov 2022 22:17:26 +0100 Subject: [PATCH 1/9] [evcc] Extend accepted types for commands & Handle quantity types of commands (#13795) * [evcc] Extend accepted command types * [evcc] Convert QuantityType commands to required units * [evcc] Allow evcc URL to end with a slash Should fix an issue on the forum, where a user sent a command of DecimalType, but the binding expected a QuantityType. See https://community.openhab.org/t/evcc-binding-electric-vehicle-charging-control/135209/15. The accepted command types are extended for all channels if possible. Signed-off-by: Florian Hotze --- .../binding/evcc/internal/EvccHandler.java | 35 ++++++++++++++++--- .../binding/evcc/internal/api/EvccAPI.java | 2 +- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccHandler.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccHandler.java index 438e8181a89d1..c75ab23221ac7 100644 --- a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccHandler.java +++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/EvccHandler.java @@ -95,16 +95,27 @@ public void handleCommand(ChannelUID channelUID, Command command) { case CHANNEL_LOADPOINT_MODE: if (command instanceof StringType) { evccAPI.setMode(loadpoint, command.toString()); + } else { + logger.debug("Command has wrong type, StringType required!"); } break; case CHANNEL_LOADPOINT_MIN_SOC: if (command instanceof QuantityType) { - evccAPI.setMinSoC(loadpoint, ((QuantityType) command).intValue()); + evccAPI.setMinSoC(loadpoint, ((QuantityType) command).toUnit(Units.PERCENT).intValue()); + } else if (command instanceof DecimalType) { + evccAPI.setMinSoC(loadpoint, ((DecimalType) command).intValue()); + } else { + logger.debug("Command has wrong type, QuantityType or DecimalType required!"); } break; case CHANNEL_LOADPOINT_TARGET_SOC: if (command instanceof QuantityType) { - evccAPI.setTargetSoC(loadpoint, ((QuantityType) command).intValue()); + evccAPI.setTargetSoC(loadpoint, + ((QuantityType) command).toUnit(Units.PERCENT).intValue()); + } else if (command instanceof DecimalType) { + evccAPI.setTargetSoC(loadpoint, ((DecimalType) command).intValue()); + } else { + logger.debug("Command has wrong type, QuantityType or DecimalType required!"); } break; case CHANNEL_LOADPOINT_TARGET_TIME: @@ -120,6 +131,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("Failed to set target charge: ", e); } } + } else { + logger.debug("Command has wrong type, DateTimeType required!"); } break; case CHANNEL_LOADPOINT_TARGET_TIME_ENABLED: @@ -129,21 +142,35 @@ public void handleCommand(ChannelUID channelUID, Command command) { } else if (command == OnOffType.OFF) { evccAPI.unsetTargetCharge(loadpoint); targetTimeEnabled = false; + } else { + logger.debug("Command has wrong type, OnOffType required!"); } break; case CHANNEL_LOADPOINT_PHASES: if (command instanceof DecimalType) { evccAPI.setPhases(loadpoint, ((DecimalType) command).intValue()); + } else { + logger.debug("Command has wrong type, DecimalType required!"); } break; case CHANNEL_LOADPOINT_MIN_CURRENT: if (command instanceof QuantityType) { - evccAPI.setMinCurrent(loadpoint, ((QuantityType) command).intValue()); + evccAPI.setMinCurrent(loadpoint, + ((QuantityType) command).toUnit(Units.AMPERE).intValue()); + } else if (command instanceof DecimalType) { + evccAPI.setMinCurrent(loadpoint, ((DecimalType) command).intValue()); + } else { + logger.debug("Command has wrong type, QuantityType or DecimalType required!"); } break; case CHANNEL_LOADPOINT_MAX_CURRENT: if (command instanceof QuantityType) { - evccAPI.setMaxCurrent(loadpoint, ((QuantityType) command).intValue()); + evccAPI.setMaxCurrent(loadpoint, + ((QuantityType) command).toUnit(Units.AMPERE).intValue()); + } else if (command instanceof DecimalType) { + evccAPI.setMaxCurrent(loadpoint, ((DecimalType) command).intValue()); + } else { + logger.debug("Command has wrong type, QuantityType or DecimalType required!"); } break; default: diff --git a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/EvccAPI.java b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/EvccAPI.java index 17c97d42a06cf..53d87edca85bf 100644 --- a/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/EvccAPI.java +++ b/bundles/org.openhab.binding.evcc/src/main/java/org/openhab/binding/evcc/internal/api/EvccAPI.java @@ -41,7 +41,7 @@ public class EvccAPI { private String host; public EvccAPI(String host) { - this.host = host; + this.host = (host.endsWith("/") ? host.substring(0, host.length() - 1) : host); } /** From 826fc9e8d70dfb8e5d9bb80cb383480a6dcf08ba Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 28 Nov 2022 15:11:40 -0700 Subject: [PATCH 2/9] [homekit] make sure to convert step values to Celsius (#13796) otherwise if your step is 1.0 in fahrenheit, then your values will get rounded to 1.0 celsius, and you might not even notice you've lost precision in the Home app. Signed-off-by: Cody Cutrer --- .../homekit/internal/HomekitTaggedItem.java | 28 +++++++++-- .../HomekitCharacteristicFactory.java | 50 +++++++------------ .../HomekitTemperatureSensorImpl.java | 5 +- .../accessories/HomekitThermostatImpl.java | 10 ++-- 4 files changed, 51 insertions(+), 42 deletions(-) diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitTaggedItem.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitTaggedItem.java index 72c668d338f06..e479b7833f648 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitTaggedItem.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitTaggedItem.java @@ -18,6 +18,8 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import javax.measure.Unit; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.items.GroupItem; @@ -415,14 +417,34 @@ public double getConfigurationAsDouble(String key, double defaultValue) { * @param defaultValue default value * @return value */ - public QuantityType getConfigurationAsQuantity(String key, QuantityType defaultValue) { + public QuantityType getConfigurationAsQuantity(String key, QuantityType defaultValue, + boolean relativeConversion) { String stringValue = getConfiguration(key, new String()); if (stringValue.isEmpty()) { return defaultValue; } var parsedValue = new QuantityType(stringValue); - var convertedValue = parsedValue.toInvertibleUnit(defaultValue.getUnit()); - // not convertible? just assume it's in the expected unit + QuantityType convertedValue; + + if (relativeConversion) { + convertedValue = parsedValue.toUnitRelative(defaultValue.getUnit()); + } else { + convertedValue = parsedValue.toInvertibleUnit(defaultValue.getUnit()); + } + // not convertible? just assume it's in the item's unit + if (convertedValue == null) { + Unit unit; + if (getBaseItem() instanceof NumberItem && (unit = ((NumberItem) getBaseItem()).getUnit()) != null) { + var bdValue = new BigDecimal(stringValue); + parsedValue = new QuantityType(bdValue, unit); + if (relativeConversion) { + convertedValue = parsedValue.toUnitRelative(defaultValue.getUnit()); + } else { + convertedValue = parsedValue.toInvertibleUnit(defaultValue.getUnit()); + } + } + } + // still not convertible? just assume it's in the default's unit if (convertedValue == null) { return new QuantityType(parsedValue.toBigDecimal(), defaultValue.getUnit()); } diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java index 823be226dcb58..8fa99252685bd 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java @@ -18,7 +18,6 @@ import java.math.RoundingMode; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -303,6 +302,11 @@ public static double convertFromCelsius(double degrees) { return convertAndRound(degrees, SIUnits.CELSIUS, useFahrenheit() ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS); } + public static double getTemperatureStep(HomekitTaggedItem taggedItem, double defaultValue) { + return taggedItem.getConfigurationAsQuantity(HomekitTaggedItem.STEP, + new QuantityType(defaultValue, SIUnits.CELSIUS), true).doubleValue(); + } + private static Supplier> getAngleSupplier(HomekitTaggedItem taggedItem, int defaultValue) { return () -> CompletableFuture.completedFuture(getAngleFromItem(taggedItem, defaultValue)); @@ -616,34 +620,16 @@ private static SaturationCharacteristic createSaturationCharacteristic(HomekitTa private static ColorTemperatureCharacteristic createColorTemperatureCharacteristic(HomekitTaggedItem taggedItem, HomekitAccessoryUpdater updater) { - // Check if units are expressed in Kelvin, not mireds, and adjust - // the min/max appropriately - Unit unit = null; - var numberItem = taggedItem.getBaseItem(); - if (numberItem instanceof NumberItem) { - unit = ((NumberItem) numberItem).getUnit(); - } - if (unit == null) { - unit = Units.MIRED; - } - final Unit finalUnit = unit; - final boolean inverted = taggedItem.isInverted(); - if (!unit.equals(Units.KELVIN) && !unit.equals(Units.MIRED)) { - logger.warn("Item {} must be in either K or mired. Given {}.", taggedItem.getName(), unit); - return new ColorTemperatureCharacteristic(null, null, null, null); - } - - var minValueQt = taggedItem.getConfigurationAsQuantity(HomekitTaggedItem.MIN_VALUE, - Objects.requireNonNull(new QuantityType(ColorTemperatureCharacteristic.DEFAULT_MIN_VALUE, Units.MIRED) - .toInvertibleUnit(unit))); - var maxValueQt = taggedItem.getConfigurationAsQuantity(HomekitTaggedItem.MAX_VALUE, - Objects.requireNonNull(new QuantityType(ColorTemperatureCharacteristic.DEFAULT_MAX_VALUE, Units.MIRED) - .toInvertibleUnit(unit))); - - int minValue = minValueQt.toInvertibleUnit(Units.MIRED).intValue(); - int maxValue = maxValueQt.toInvertibleUnit(Units.MIRED).intValue(); + int minValue = taggedItem + .getConfigurationAsQuantity(HomekitTaggedItem.MIN_VALUE, + new QuantityType(ColorTemperatureCharacteristic.DEFAULT_MIN_VALUE, Units.MIRED), false) + .intValue(); + int maxValue = taggedItem + .getConfigurationAsQuantity(HomekitTaggedItem.MAX_VALUE, + new QuantityType(ColorTemperatureCharacteristic.DEFAULT_MAX_VALUE, Units.MIRED), false) + .intValue(); // It's common to swap these if you're providing in Kelvin instead of mired if (minValue > maxValue) { @@ -804,9 +790,8 @@ private static CoolingThresholdTemperatureCharacteristic createCoolingThresholdC HomekitTaggedItem.MIN_VALUE, CoolingThresholdTemperatureCharacteristic.DEFAULT_MIN_VALUE)); double maxValue = HomekitCharacteristicFactory.convertToCelsius(taggedItem.getConfigurationAsDouble( HomekitTaggedItem.MAX_VALUE, CoolingThresholdTemperatureCharacteristic.DEFAULT_MAX_VALUE)); - return new CoolingThresholdTemperatureCharacteristic(minValue, maxValue, - taggedItem.getConfigurationAsDouble(HomekitTaggedItem.STEP, - CoolingThresholdTemperatureCharacteristic.DEFAULT_STEP), + double step = getTemperatureStep(taggedItem, CoolingThresholdTemperatureCharacteristic.DEFAULT_STEP); + return new CoolingThresholdTemperatureCharacteristic(minValue, maxValue, step, getTemperatureSupplier(taggedItem, minValue), setTemperatureConsumer(taggedItem), getSubscriber(taggedItem, COOLING_THRESHOLD_TEMPERATURE, updater), getUnsubscriber(taggedItem, COOLING_THRESHOLD_TEMPERATURE, updater)); @@ -818,9 +803,8 @@ private static HeatingThresholdTemperatureCharacteristic createHeatingThresholdC HomekitTaggedItem.MIN_VALUE, HeatingThresholdTemperatureCharacteristic.DEFAULT_MIN_VALUE)); double maxValue = HomekitCharacteristicFactory.convertToCelsius(taggedItem.getConfigurationAsDouble( HomekitTaggedItem.MAX_VALUE, HeatingThresholdTemperatureCharacteristic.DEFAULT_MAX_VALUE)); - return new HeatingThresholdTemperatureCharacteristic(minValue, maxValue, - taggedItem.getConfigurationAsDouble(HomekitTaggedItem.STEP, - HeatingThresholdTemperatureCharacteristic.DEFAULT_STEP), + double step = getTemperatureStep(taggedItem, HeatingThresholdTemperatureCharacteristic.DEFAULT_STEP); + return new HeatingThresholdTemperatureCharacteristic(minValue, maxValue, step, getTemperatureSupplier(taggedItem, minValue), setTemperatureConsumer(taggedItem), getSubscriber(taggedItem, HEATING_THRESHOLD_TEMPERATURE, updater), getUnsubscriber(taggedItem, HEATING_THRESHOLD_TEMPERATURE, updater)); diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java index 2be10469c1ec6..594d05a98b953 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java @@ -75,8 +75,9 @@ public double getMaxCurrentTemperature() { @Override public double getMinStepCurrentTemperature() { - return getAccessoryConfiguration(HomekitCharacteristicType.CURRENT_TEMPERATURE, HomekitTaggedItem.STEP, - BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_STEP)).doubleValue(); + return HomekitCharacteristicFactory.getTemperatureStep( + getCharacteristic(HomekitCharacteristicType.CURRENT_TEMPERATURE).get(), + TargetTemperatureCharacteristic.DEFAULT_STEP); } @Override diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java index a227fef7bf4e4..27c9617d5aed3 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java @@ -134,8 +134,9 @@ public double getMaxCurrentTemperature() { @Override public double getMinStepCurrentTemperature() { - return getAccessoryConfiguration(HomekitCharacteristicType.CURRENT_TEMPERATURE, HomekitTaggedItem.STEP, - BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_STEP)).doubleValue(); + return HomekitCharacteristicFactory.getTemperatureStep( + getCharacteristic(HomekitCharacteristicType.CURRENT_TEMPERATURE).get(), + TargetTemperatureCharacteristic.DEFAULT_STEP); } @Override @@ -204,8 +205,9 @@ public double getMaxTargetTemperature() { @Override public double getMinStepTargetTemperature() { - return getAccessoryConfiguration(HomekitCharacteristicType.TARGET_TEMPERATURE, HomekitTaggedItem.STEP, - BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_STEP)).doubleValue(); + return HomekitCharacteristicFactory.getTemperatureStep( + getCharacteristic(HomekitCharacteristicType.TARGET_TEMPERATURE).get(), + TargetTemperatureCharacteristic.DEFAULT_STEP); } @Override From 3912487305794bbc926cbaaf0c2d1ef8e33dece6 Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Mon, 28 Nov 2022 23:38:55 +0100 Subject: [PATCH 3/9] [jdbc] Add safety valve for suspicious migrations (#13797) * Abort migration from real names when most tables have table name prefix * Add missing checks for database connection from console commands * Add additional documentation for check/fix schema Signed-off-by: Jacob Laursen --- .../org.openhab.persistence.jdbc/README.md | 6 +++++ .../persistence/jdbc/internal/JdbcMapper.java | 22 ++++++++++++++++++- .../jdbc/internal/JdbcPersistenceService.java | 16 ++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.persistence.jdbc/README.md b/bundles/org.openhab.persistence.jdbc/README.md index fbb4ac3d10695..611814358f8bd 100644 --- a/bundles/org.openhab.persistence.jdbc/README.md +++ b/bundles/org.openhab.persistence.jdbc/README.md @@ -214,6 +214,12 @@ Use the command `jdbc schema check` to perform an integrity check of the schema. Identified issues can be fixed automatically using the command `jdbc schema fix` (all items having issues) or `jdbc schema fix ` (single item). +Issues than can be identified and possibly fixed: + +- Wrong column name case (`time` and `name`). +- Wrong column type. Before fixing this, make sure that time-zone is correctly configured. +- Unexpected column (identify only). + ### For Developers * Clearly separated source files for the database-specific part of openHAB logic. diff --git a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/JdbcMapper.java b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/JdbcMapper.java index 1b3c165226284..7a510527fa394 100644 --- a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/JdbcMapper.java +++ b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/JdbcMapper.java @@ -49,8 +49,9 @@ */ @NonNullByDefault public class JdbcMapper { - private final Logger logger = LoggerFactory.getLogger(JdbcMapper.class); + private static final int MIGRATION_PERCENTAGE_THRESHOLD = 50; + private final Logger logger = LoggerFactory.getLogger(JdbcMapper.class); private final TimeZoneProvider timeZoneProvider; // Error counter - used to reconnect to database on error @@ -407,6 +408,25 @@ private void formatTableNames() throws JdbcSQLException { initialized = tmpinit; return; } + // Safety valve to prevent accidental migrations. + int numberOfTables = itemTables.size(); + if (numberOfTables > 0) { + String prefix = conf.getTableNamePrefix(); + long numberOfItemsWithPrefix = itemTables.stream() + .filter(i -> i.startsWith(prefix) || i.toLowerCase().startsWith("item")).count(); + long percentageWithPrefix = (numberOfItemsWithPrefix * 100) / itemTables.size(); + if (!prefix.isBlank() && percentageWithPrefix >= MIGRATION_PERCENTAGE_THRESHOLD) { + logger.error( + "JDBC::formatTableNames: {}% of all tables start with table name prefix '{}' or 'item', but items manage table '{}' was not found or is empty. Check configuration parameter 'itemsManageTable'", + percentageWithPrefix, conf.getTableNamePrefix(), conf.getItemsManageTable()); + if (ifTableExists("items")) { + logger.error( + "JDBC::formatTableNames: Table 'items' was found, consider updating configuration parameter 'itemsManageTable' accordingly"); + } + initialized = tmpinit; + return; + } + } oldNewTableNames = new ArrayList<>(); for (String itemName : itemTables) { ItemsVO isvo = new ItemsVO(); diff --git a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/JdbcPersistenceService.java b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/JdbcPersistenceService.java index af7da62f953cc..28fc2c5a9730a 100644 --- a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/JdbcPersistenceService.java +++ b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/JdbcPersistenceService.java @@ -322,6 +322,12 @@ public Map getItemNameToTableNameMap() { */ public Collection getSchemaIssues(String tableName, String itemName) throws JdbcSQLException { List issues = new ArrayList<>(); + + if (!checkDBAccessability()) { + logger.warn("JDBC::getSchemaIssues: database not connected"); + return issues; + } + Item item; try { item = itemRegistry.getItem(itemName); @@ -375,6 +381,11 @@ public Collection getSchemaIssues(String tableName, String itemName) thr * @throws JdbcSQLException on SQL errors */ public boolean fixSchemaIssues(String tableName, String itemName) throws JdbcSQLException { + if (!checkDBAccessability()) { + logger.warn("JDBC::fixSchemaIssues: database not connected"); + return false; + } + Item item; try { item = itemRegistry.getItem(itemName); @@ -469,6 +480,11 @@ private ItemTableCheckEntry getCheckedEntry(String itemName, String tableName, b * @throws JdbcSQLException */ public boolean cleanupItem(String itemName, boolean force) throws JdbcSQLException { + if (!checkDBAccessability()) { + logger.warn("JDBC::cleanupItem: database not connected"); + return false; + } + String tableName = itemNameToTableNameMap.get(itemName); if (tableName == null) { return false; From 735089a7c71b7b45da89c8581d8d68294bdbec6a Mon Sep 17 00:00:00 2001 From: jimtng <2554958+jimtng@users.noreply.github.com> Date: Tue, 29 Nov 2022 08:51:19 +1000 Subject: [PATCH 4/9] [jrubyscripting] Allow multiple version specifiers for gems (#13779) Signed-off-by: Jimmy Tanagra --- .../README.md | 23 ++++++++++++------- .../JRubyScriptEngineConfiguration.java | 13 +++++++---- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/bundles/org.openhab.automation.jrubyscripting/README.md b/bundles/org.openhab.automation.jrubyscripting/README.md index 7c1df5d6f4d09..5225683bc4a61 100644 --- a/bundles/org.openhab.automation.jrubyscripting/README.md +++ b/bundles/org.openhab.automation.jrubyscripting/README.md @@ -8,14 +8,14 @@ After installing this add-on, you will find configuration options in the openHAB Alternatively, JRuby configuration parameters may be set by creating a `jruby.cfg` file in `conf/services/` -| Parameter | Default | Description | -| ----------------------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| org.openhab.automation.jrubyscripting:gem_home | $OPENHAB_CONF/scripts/lib/ruby/gem_home | Location ruby gems will be installed and loaded, directory will be created if missing and gem installs are specified | -| org.openhab.automation.jrubyscripting:rubylib | $OPENHAB_CONF/automation/lib/ruby/ | Search path for user libraries. Separate each path with a colon (semicolon in Windows). | -| org.openhab.automation.jrubyscripting:local_context | singlethread | The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See [this](https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type) for options and details | -| org.openhab.automation.jrubyscripting:local_variables | transient | Defines how variables are shared between Ruby and Java. See [this](https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options) for options and details | -| org.openhab.automation.jrubyscripting:gems | | A comma separated list of [Ruby Gems](https://rubygems.org/) to install. | -| org.openhab.automation.jrubyscripting:require | | A comma separated list of script names to be required by the JRuby Scripting Engine at the beginning of user scripts. | +| Parameter | Default | Description | +| ----------------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| org.openhab.automation.jrubyscripting:gem_home | $OPENHAB_CONF/scripts/lib/ruby/gem_home | Location ruby gems will be installed and loaded, directory will be created if missing and gem installs are specified | +| org.openhab.automation.jrubyscripting:rubylib | $OPENHAB_CONF/automation/lib/ruby/ | Search path for user libraries. Separate each path with a colon (semicolon in Windows). | +| org.openhab.automation.jrubyscripting:local_context | singlethread | The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See [this](https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type) for options and details | +| org.openhab.automation.jrubyscripting:local_variables | transient | Defines how variables are shared between Ruby and Java. See [this](https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options) for options and details | +| org.openhab.automation.jrubyscripting:gems | | A comma separated list of [Ruby Gems](https://rubygems.org/) to install. | +| org.openhab.automation.jrubyscripting:require | | A comma separated list of script names to be required by the JRuby Scripting Engine at the beginning of user scripts. | | org.openhab.automation.jrubyscripting:check_update | true | Check RubyGems for updates to the above gems when OpenHAB starts or JRuby settings are changed. Otherwise it will try to fulfil the requirements with locally installed gems, and you can manage them yourself with an external Ruby by setting the same GEM_HOME. | ## Ruby Gems @@ -23,6 +23,7 @@ Alternatively, JRuby configuration parameters may be set by creating a `jruby.cf This automation add-on will install user specified gems and make them available on the library search path. Gem versions may be specified using the standard ruby gem_name=version format. The version number follows the [pessimistic version constraint](https://guides.rubygems.org/patterns/#pessimistic-version-constraint) syntax. +Multiple version specifiers can be added by separating them with a semicolon. For example this configuration will install the latest version of the [openHAB JRuby Scripting Library](https://boc-tothefuture.github.io/openhab-jruby/), and instruct the scripting engine to automatically insert `require 'openhab'` at the start of the script. @@ -31,6 +32,12 @@ org.openhab.automation.jrubyscripting:gems=openhab-scripting org.openhab.automation.jrubyscripting:require=openhab ``` +Example with multiple version specifiers: + +```text +org.openhab.automation.jrubyscripting:gems=library= >= 2.2.0; < 3.0, another-gem= > 4.0.0.a; < 5 +``` + ## Creating JRuby Scripts When this add-on is installed, you can select JRuby as a scripting language when creating a script action within the rule editor of the UI. diff --git a/bundles/org.openhab.automation.jrubyscripting/src/main/java/org/openhab/automation/jrubyscripting/internal/JRubyScriptEngineConfiguration.java b/bundles/org.openhab.automation.jrubyscripting/src/main/java/org/openhab/automation/jrubyscripting/internal/JRubyScriptEngineConfiguration.java index 96488ac598a1d..f5263329ed60a 100644 --- a/bundles/org.openhab.automation.jrubyscripting/src/main/java/org/openhab/automation/jrubyscripting/internal/JRubyScriptEngineConfiguration.java +++ b/bundles/org.openhab.automation.jrubyscripting/src/main/java/org/openhab/automation/jrubyscripting/internal/JRubyScriptEngineConfiguration.java @@ -171,11 +171,11 @@ private synchronized void configureGems(ScriptEngine engine) { int validGems = 0; for (String gem : gems) { gem = gem.trim(); - String version = ""; + String[] versions = {}; if (gem.contains("=")) { - String[] gemParts = gem.split("="); + String[] gemParts = gem.split("=", 2); gem = gemParts[0].trim(); - version = gemParts[1].trim(); + versions = gemParts[1].split(";"); } if (gem.isEmpty()) { @@ -183,8 +183,11 @@ private synchronized void configureGems(ScriptEngine engine) { } gemCommand += " gem '" + gem + "'"; - if (!version.isEmpty()) { - gemCommand += ", '" + version + "'"; + for (String version : versions) { + version = version.trim(); + if (!version.isEmpty()) { + gemCommand += ", '" + version + "'"; + } } gemCommand += ", require: false\n"; validGems += 1; From 6bdcd15d9963479ff2afe6dc5806dd5c90523c2e Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 28 Nov 2022 16:33:49 -0700 Subject: [PATCH 5/9] [mqtt.espmilight] Automatically convert color values to color temp (#13578) * [mqtt.espmilight] Automatically convert color values to color temp for RGB+CCT bulbs Yes, it's lots of math, but references are provided. This supplants whiteThreshold for RGB+CCT bulbs since it is far more flexible and accurate. Signed-off-by: Cody Cutrer --- .../README.md | 1 + .../espmilighthub/internal/ConfigOptions.java | 7 +- .../handler/EspMilightHubHandler.java | 208 ++++++++++++++++-- .../main/resources/OH-INF/config/config.xml | 24 +- .../resources/OH-INF/i18n/mqtt.properties | 14 +- .../resources/OH-INF/thing/thing-types.xml | 4 +- 6 files changed, 216 insertions(+), 42 deletions(-) diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/README.md b/bundles/org.openhab.binding.mqtt.espmilighthub/README.md index 2928a7db0e09b..7de8c6c8b676e 100644 --- a/bundles/org.openhab.binding.mqtt.espmilighthub/README.md +++ b/bundles/org.openhab.binding.mqtt.espmilighthub/README.md @@ -106,6 +106,7 @@ Note that the group 0 (or ALL group) is not auto discovered as a thing and thus | `oneTriggersNightMode` | Night mode is a much lower level of light and this feature allows it to be auto selected when your fader/slider moves to 1%. NOTE: Night mode by design locks out some controls of a physical remote, so this feature is disabled by default. | Y | false | | `powerFailsToMinimum` | If lights loose power from the power switch OR a power outage, they will default to using the lowest brightness if the light was turned off before the power failure occurred. | Y | true | | `whiteThreshold` | This feature allows you to use a color control to change to using the real white LEDs when the saturation is equal to, or below this threshold. -1 will disable this feature. | Y | 12 | +| `duvThreshold` | This feature allows you to use a color control to change to using the real warm/cool white LEDs to set a white color temperature if the color is within a certain threshold of the block body curve. 1 will effectively disable this feature. The default settings maps well to Apple's HomeKit that will allow you to choose a color temperature, but sends it as an HSB value. See https://www.waveformlighting.com/tech/calculate-duv-from-cie-1931-xy-coordinates/ for more information. | Y | 0.003 | ## Channels diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/ConfigOptions.java b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/ConfigOptions.java index 1364b7d9a5d68..878279b25ecd8 100644 --- a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/ConfigOptions.java +++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/ConfigOptions.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.mqtt.espmilighthub.internal; +import java.math.BigDecimal; + import org.eclipse.jdt.annotation.NonNullByDefault; /** @@ -21,9 +23,10 @@ */ @NonNullByDefault public class ConfigOptions { + public BigDecimal duvThreshold = new BigDecimal("0.003"); public int whiteThreshold = -1; - public int whiteSat = 32; - public int whiteHue = 35; + public int whiteSat = -1; + public int whiteHue = -1; public int favouriteWhite = 200; public boolean oneTriggersNightMode = false; public boolean powerFailsToMinimum = false; diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java index e4f2186bab17a..e505a9d83b9a1 100644 --- a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java +++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java @@ -17,6 +17,7 @@ import static org.openhab.binding.mqtt.espmilighthub.internal.EspMilightHubBindingConstants.*; import java.math.BigDecimal; +import java.math.MathContext; import java.nio.charset.StandardCharsets; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -28,6 +29,7 @@ import org.openhab.core.io.transport.mqtt.MqttConnectionObserver; import org.openhab.core.io.transport.mqtt.MqttConnectionState; import org.openhab.core.io.transport.mqtt.MqttMessageSubscriber; +import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.IncreaseDecreaseType; import org.openhab.core.library.types.OnOffType; @@ -56,6 +58,46 @@ */ @NonNullByDefault public class EspMilightHubHandler extends BaseThingHandler implements MqttConnectionObserver, MqttMessageSubscriber { + // these are all constants used in color conversion calcuations. + // strings are necessary to prevent floating point loss of precision + private static final BigDecimal BIG_DECIMAL_THOUSAND = new BigDecimal(1000); + private static final BigDecimal BIG_DECIMAL_MILLION = new BigDecimal(1000000); + + private static final BigDecimal[][] KANG_X_COEFFICIENTS = { + { new BigDecimal("-3.0258469"), new BigDecimal("2.1070379"), new BigDecimal("0.2226347"), + new BigDecimal("0.24039") }, + { new BigDecimal("-0.2661239"), new BigDecimal("-0.234589"), new BigDecimal("0.8776956"), + new BigDecimal("0.179910") } }; + + private static final BigDecimal[][] KANG_Y_COEFFICIENTS = { + { new BigDecimal("3.0817580"), new BigDecimal("-5.8733867"), new BigDecimal("3.75112997"), + new BigDecimal("-0.37001483") }, + { new BigDecimal("-0.9549476"), new BigDecimal("-1.37418593"), new BigDecimal("2.09137015"), + new BigDecimal("-0.16748867") }, + { new BigDecimal("-1.1063814"), new BigDecimal("-1.34811020"), new BigDecimal("2.18555832"), + new BigDecimal("-0.20219683") } }; + + private static final BigDecimal BIG_DECIMAL_03320 = new BigDecimal("0.3320"); + private static final BigDecimal BIG_DECIMAL_01858 = new BigDecimal("0.1858"); + private static final BigDecimal[] MCCAMY_COEFFICIENTS = { new BigDecimal(437), new BigDecimal(3601), + new BigDecimal(6862), new BigDecimal(5517) }; + + private static final BigDecimal BIG_DECIMAL_2 = new BigDecimal(2); + private static final BigDecimal BIG_DECIMAL_3 = new BigDecimal(3); + private static final BigDecimal BIG_DECIMAL_4 = new BigDecimal(4); + private static final BigDecimal BIG_DECIMAL_6 = new BigDecimal(6); + private static final BigDecimal BIG_DECIMAL_12 = new BigDecimal(12); + + private static final BigDecimal BIG_DECIMAL_0292 = new BigDecimal("0.292"); + private static final BigDecimal BIG_DECIMAL_024 = new BigDecimal("0.24"); + + private static final BigDecimal[] CORM_COEFFICIENTS = { new BigDecimal("-0.00616793"), new BigDecimal("0.0893944"), + new BigDecimal("-0.5179722"), new BigDecimal("1.5317403"), new BigDecimal("-2.4243787"), + new BigDecimal("1.925865"), new BigDecimal("-0.471106") }; + + private static final BigDecimal BIG_DECIMAL_153 = new BigDecimal(153); + private static final BigDecimal BIG_DECIMAL_217 = new BigDecimal(217); + private final Logger logger = LoggerFactory.getLogger(this.getClass()); private @Nullable MqttBrokerConnection connection; private ThingRegistry thingRegistry; @@ -106,16 +148,15 @@ void changeChannel(String channel, State state) { private void processIncomingState(String messageJSON) { // Need to handle State and Level at the same time to process level=0 as off// - BigDecimal tempBulbLevel = BigDecimal.ZERO; + PercentType tempBulbLevel = PercentType.ZERO; String bulbState = Helper.resolveJSON(messageJSON, "\"state\":\"", 3); String bulbLevel = Helper.resolveJSON(messageJSON, "\"level\":", 3); if (!bulbLevel.isEmpty()) { if ("0".equals(bulbLevel) || "OFF".equals(bulbState)) { changeChannel(CHANNEL_LEVEL, OnOffType.OFF); - tempBulbLevel = BigDecimal.ZERO; } else { - tempBulbLevel = new BigDecimal(bulbLevel); - changeChannel(CHANNEL_LEVEL, new PercentType(tempBulbLevel)); + tempBulbLevel = new PercentType(Integer.valueOf(bulbLevel)); + changeChannel(CHANNEL_LEVEL, tempBulbLevel); } } else if ("ON".equals(bulbState) || "OFF".equals(bulbState)) { // NOTE: Level is missing when this runs changeChannel(CHANNEL_LEVEL, OnOffType.valueOf(bulbState)); @@ -123,15 +164,17 @@ private void processIncomingState(String messageJSON) { bulbMode = Helper.resolveJSON(messageJSON, "\"bulb_mode\":\"", 5); switch (bulbMode) { case "white": - if (!"cct".equals(globeType) && !"fut091".equals(globeType)) { + if (hasRGB()) { changeChannel(CHANNEL_BULB_MODE, new StringType("white")); - changeChannel(CHANNEL_COLOUR, new HSBType("0,0," + tempBulbLevel)); changeChannel(CHANNEL_DISCO_MODE, new StringType("None")); } - String bulbCTemp = Helper.resolveJSON(messageJSON, "\"color_temp\":", 3); - if (!bulbCTemp.isEmpty()) { - int ibulbCTemp = (int) Math.round(((Float.valueOf(bulbCTemp) / 2.17) - 171) * -1); - changeChannel(CHANNEL_COLOURTEMP, new PercentType(ibulbCTemp)); + String bulbCTempS = Helper.resolveJSON(messageJSON, "\"color_temp\":", 3); + if (!bulbCTempS.isEmpty()) { + var bulbCTemp = Integer.valueOf(bulbCTempS); + changeChannel(CHANNEL_COLOURTEMP, scaleMireds(bulbCTemp)); + if (hasRGB()) { + changeChannel(CHANNEL_COLOUR, calculateHSBFromColorTemp(bulbCTemp, tempBulbLevel)); + } } break; case "color": @@ -145,11 +188,20 @@ private void processIncomingState(String messageJSON) { if (bulbSaturation.isEmpty()) { bulbSaturation = "100"; } - changeChannel(CHANNEL_COLOUR, new HSBType(bulbHue + "," + bulbSaturation + "," + tempBulbLevel)); + // 360 isn't allowed by OpenHAB + if (bulbHue.equals("360")) { + bulbHue = "0"; + } + var hsb = new HSBType(new DecimalType(Integer.valueOf(bulbHue)), + new PercentType(Integer.valueOf(bulbSaturation)), tempBulbLevel); + changeChannel(CHANNEL_COLOUR, hsb); + if (hasCCT()) { + changeChannel(CHANNEL_COLOURTEMP, scaleMireds(calculateColorTempFromHSB(hsb))); + } } break; case "scene": - if (!"cct".equals(globeType) && !"fut091".equals(globeType)) { + if (hasRGB()) { changeChannel(CHANNEL_BULB_MODE, new StringType("scene")); } String bulbDiscoMode = Helper.resolveJSON(messageJSON, "\"mode\":", 1); @@ -158,7 +210,7 @@ private void processIncomingState(String messageJSON) { } break; case "night": - if (!"cct".equals(globeType) && !"fut091".equals(globeType)) { + if (hasRGB()) { changeChannel(CHANNEL_BULB_MODE, new StringType("night")); if (config.oneTriggersNightMode) { changeChannel(CHANNEL_LEVEL, new PercentType("1")); @@ -168,6 +220,113 @@ private void processIncomingState(String messageJSON) { } } + private boolean hasCCT() { + switch (globeType) { + case "rgb_cct": + case "cct": + case "fut089": + case "fut091": + return true; + default: + return false; + } + } + + private boolean hasRGB() { + switch (globeType) { + case "rgb_cct": + case "rgb": + case "rgbw": + case "fut089": + return true; + default: + return false; + } + } + + /** + * Scales mireds to 0-100% + */ + private static PercentType scaleMireds(int mireds) { + // range in mireds is 153-370 + // 100 - (mireds - 153) / (370 - 153) * 100 + if (mireds >= 370) { + return PercentType.HUNDRED; + } else if (mireds <= 153) { + return PercentType.ZERO; + } + return new PercentType(BIG_DECIMAL_100.subtract(new BigDecimal(mireds).subtract(BIG_DECIMAL_153) + .divide(BIG_DECIMAL_217, MathContext.DECIMAL128).multiply(BIG_DECIMAL_100))); + } + + private static BigDecimal polynomialFit(BigDecimal x, BigDecimal[] coefficients) { + var result = BigDecimal.ZERO; + var xAccumulator = BigDecimal.ONE; + // forms K[4]*x^0 + K[3]*x^1 + K[2]*x^2 + K[1]*x^3 + K[0]*x^4 + // (or reverse the order of terms for the usual way of writing it in academic papers) + for (int i = coefficients.length - 1; i >= 0; i--) { + result = result.add(coefficients[i].multiply(xAccumulator)); + xAccumulator = xAccumulator.multiply(x); + } + return result; + } + + // https://www.jkps.or.kr/journal/download_pdf.php?spage=865&volume=41&number=6 (8) and (9) + private static HSBType calculateHSBFromColorTemp(int mireds, PercentType brightness) { + var cct = BIG_DECIMAL_MILLION.divide(new BigDecimal(mireds), MathContext.DECIMAL128); + var cctInt = cct.intValue(); + + BigDecimal[] coefficients; + // 1667K to 4000K and 4000K to 25000K; no range checks since our mired range fits within this + if (cctInt <= 4000) { + coefficients = KANG_X_COEFFICIENTS[1]; + } else { + coefficients = KANG_X_COEFFICIENTS[0]; + } + BigDecimal x = polynomialFit(BIG_DECIMAL_THOUSAND.divide(cct, MathContext.DECIMAL128), coefficients); + + if (cctInt <= 2222) { + coefficients = KANG_Y_COEFFICIENTS[2]; + } else if (cctInt <= 4000) { + coefficients = KANG_Y_COEFFICIENTS[1]; + } else { + coefficients = KANG_Y_COEFFICIENTS[0]; + } + BigDecimal y = polynomialFit(x, coefficients); + var rawHsb = HSBType.fromXY(x.floatValue() * 100.0f, y.floatValue() * 100.0f); + return new HSBType(rawHsb.getHue(), rawHsb.getSaturation(), brightness); + } + + // https://www.waveformlighting.com/tech/calculate-color-temperature-cct-from-cie-1931-xy-coordinates/ + private static int calculateColorTempFromHSB(HSBType hsb) { + PercentType[] xy = hsb.toXY(); + var x = xy[0].toBigDecimal().divide(BIG_DECIMAL_100); + var y = xy[1].toBigDecimal().divide(BIG_DECIMAL_100); + var n = x.subtract(BIG_DECIMAL_03320).divide(BIG_DECIMAL_01858.subtract(y), MathContext.DECIMAL128); + BigDecimal cctK = polynomialFit(n, MCCAMY_COEFFICIENTS); + return BIG_DECIMAL_MILLION.divide(cctK, MathContext.DECIMAL128).round(new MathContext(0)).intValue(); + } + + // https://cormusa.org/wp-content/uploads/2018/04/CORM_2011_Calculation_of_CCT_and_Duv_and_Practical_Conversion_Formulae.pdf + // page 19 + private static BigDecimal calculateDuvFromHSB(HSBType hsb) { + PercentType[] xy = hsb.toXY(); + var x = xy[0].toBigDecimal().divide(BIG_DECIMAL_100); + var y = xy[1].toBigDecimal().divide(BIG_DECIMAL_100); + var u = BIG_DECIMAL_4.multiply(x).divide( + BIG_DECIMAL_2.multiply(x).negate().add(BIG_DECIMAL_12.multiply(y).add(BIG_DECIMAL_3)), + MathContext.DECIMAL128); + var v = BIG_DECIMAL_6.multiply(y).divide( + BIG_DECIMAL_2.multiply(x).negate().add(BIG_DECIMAL_12.multiply(y).add(BIG_DECIMAL_3)), + MathContext.DECIMAL128); + var Lfp = u.subtract(BIG_DECIMAL_0292).pow(2).add(v.subtract(BIG_DECIMAL_024).pow(2)) + .sqrt(MathContext.DECIMAL128); + var a = new BigDecimal( + Math.acos(u.subtract(BIG_DECIMAL_0292).divide(Lfp, MathContext.DECIMAL128).doubleValue())); + BigDecimal Lbb = polynomialFit(a, CORM_COEFFICIENTS); + return Lfp.subtract(Lbb); + } + /* * Used to calculate the colour temp for a globe if you want the light to get warmer as it is dimmed like a * traditional halogen globe @@ -187,6 +346,8 @@ void turnOff() { } void handleLevelColour(Command command) { + int mireds; + if (command instanceof OnOffType) { if (OnOffType.ON.equals(command)) { sendMQTT("{\"state\":\"ON\",\"level\":" + savedLevel + "}"); @@ -210,7 +371,7 @@ void handleLevelColour(Command command) { HSBType hsb = (HSBType) command; // This feature allows google home or Echo to trigger white mode when asked to turn color to white. if (hsb.getHue().intValue() == config.whiteHue && hsb.getSaturation().intValue() == config.whiteSat) { - if ("rgb_cct".equals(globeType) || "fut089".equals(globeType)) { + if (hasCCT()) { sendMQTT("{\"state\":\"ON\",\"color_temp\":" + config.favouriteWhite + "}"); } else {// globe must only have 1 type of white sendMQTT("{\"command\":\"set_white\"}"); @@ -219,6 +380,10 @@ void handleLevelColour(Command command) { } else if (PercentType.ZERO.equals(hsb.getBrightness())) { turnOff(); return; + } else if (config.duvThreshold.compareTo(BigDecimal.ONE) < 0 + && calculateDuvFromHSB(hsb).abs().compareTo(config.duvThreshold) <= 0 + && (mireds = calculateColorTempFromHSB(hsb)) >= 153 && mireds <= 370) { + sendMQTT("{\"state\":\"ON\",\"level\":" + hsb.getBrightness() + ",\"color_temp\":" + mireds + "}"); } else if (config.whiteThreshold != -1 && hsb.getSaturation().intValue() <= config.whiteThreshold) { sendMQTT("{\"command\":\"set_white\"}");// Can't send the command and level in the same message. sendMQTT("{\"level\":" + hsb.getBrightness().intValue() + "}"); @@ -239,7 +404,7 @@ void handleLevelColour(Command command) { } sendMQTT("{\"state\":\"ON\",\"level\":" + command + "}"); savedLevel = percentType.toBigDecimal(); - if ("rgb_cct".equals(globeType) || "fut089".equals(globeType)) { + if (hasCCT()) { if (config.dimmedCT > 0 && "white".equals(bulbMode)) { sendMQTT("{\"state\":\"ON\",\"color_temp\":" + autoColourTemp(savedLevel.intValue()) + "}"); } @@ -254,9 +419,10 @@ public void handleCommand(ChannelUID channelUID, Command command) { return; } switch (channelUID.getId()) { + case CHANNEL_COLOUR: case CHANNEL_LEVEL: handleLevelColour(command); - return; + break; case CHANNEL_BULB_MODE: bulbMode = command.toString(); break; @@ -270,8 +436,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { case CHANNEL_DISCO_MODE: sendMQTT("{\"mode\":\"" + command + "\"}"); break; - case CHANNEL_COLOUR: - handleLevelColour(command); } } @@ -322,8 +486,12 @@ private void sendMQTT(String payload) { @Override public void processMessage(String topic, byte[] payload) { String state = new String(payload, StandardCharsets.UTF_8); - logger.trace("Recieved the following new Milight state:{}:{}", topic, state); - processIncomingState(state); + logger.trace("Received the following new Milight state:{}:{}", topic, state); + try { + processIncomingState(state); + } catch (Exception e) { + logger.warn("Failed processing Milight state {} for {}", state, topic, e); + } } @Override diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/config/config.xml index f6cf7d88e04ed..6a809ff12ad4f 100644 --- a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/config/config.xml @@ -13,7 +13,7 @@ - If lights loose power when soft off, the lights will default back to the minimum brightness. + If lights lose power when soft off, the lights will default back to the minimum brightness. false @@ -37,23 +37,25 @@ When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS. - 35 + -1 When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS. - 32 + -1 - - - Saturation values at or below this value on a RGBW color control will trigger the white mode. -1 will - disable - this feature. + + + this link for more information on how this is calculated. + ]]> - 6 + 0.003 @@ -76,7 +78,7 @@ - If lights loose power, the lights will turn on to the minimum brightness. + If lights lose power, the lights will turn on to the minimum brightness. true @@ -104,7 +106,7 @@ - If lights loose power, the lights will turn on to the minimum brightness. + If lights lose power, the lights will turn on to the minimum brightness. false diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/i18n/mqtt.properties b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/i18n/mqtt.properties index 555b7126cb2ef..6b5bd216cfb14 100644 --- a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/i18n/mqtt.properties +++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/i18n/mqtt.properties @@ -1,7 +1,7 @@ # thing types thing-type.mqtt.cct.label = Milight CCT -thing-type.mqtt.cct.description = Led globe with both cool and warm white controls +thing-type.mqtt.cct.description = LED globe with both cool and warm white controls thing-type.mqtt.fut089.label = Milight FUT089 thing-type.mqtt.fut089.description = Use this when your remote is the newer 8 group type called FUT089 and your globes are rgb_cct thing-type.mqtt.fut091.label = Milight FUT091 @@ -9,7 +9,7 @@ thing-type.mqtt.fut091.description = Use this when your remote is the newer fut0 thing-type.mqtt.rgb.label = Milight RGB thing-type.mqtt.rgb.description = RGB Globe with no white thing-type.mqtt.rgb_cct.label = Milight RGBCCT -thing-type.mqtt.rgb_cct.description = Led globe with full Colour, and both cool and warm whites. +thing-type.mqtt.rgb_cct.description = LED globe with full Colour, and both cool and warm whites. thing-type.mqtt.rgbw.label = Milight RGBW thing-type.mqtt.rgbw.description = RGB Globe with a fixed white @@ -22,25 +22,25 @@ thing-type.config.mqtt.cct.oneTriggersNightMode.description = 1% on a slider wil thing-type.config.mqtt.rgb.oneTriggersNightMode.label = 1% Triggers Night Mode thing-type.config.mqtt.rgb.oneTriggersNightMode.description = 1% on a slider will trigger the Night Mode. thing-type.config.mqtt.rgb.powerFailsToMinimum.label = Dimmed on Power Fail -thing-type.config.mqtt.rgb.powerFailsToMinimum.description = If lights loose power when soft off, the lights will default back to the minimum brightness. +thing-type.config.mqtt.rgb.powerFailsToMinimum.description = If lights lose power when soft off, the lights will default back to the minimum brightness. thing-type.config.mqtt.rgbandcct.dimmedCT.label = Dimmed Colour Temp thing-type.config.mqtt.rgbandcct.dimmedCT.description = Traditional globes grow warmer the more they are dimmed. Set this to 370, or leave blank to disable. +thing-type.config.mqtt.rgbandcct.duvThreshold.label = Duv Threshold +thing-type.config.mqtt.rgbandcct.duvThreshold.description = Duv values at or below this value on a RGBWW color control will trigger white mode at the appropriate color temperature. 1 will effectively disable this feature. See this link for more information on how this is calculated. thing-type.config.mqtt.rgbandcct.favouriteWhite.label = Favourite White thing-type.config.mqtt.rgbandcct.favouriteWhite.description = When a shortcut triggers white mode, use this for the colour white. thing-type.config.mqtt.rgbandcct.oneTriggersNightMode.label = 1% Triggers Night Mode thing-type.config.mqtt.rgbandcct.oneTriggersNightMode.description = 1% on a slider will trigger the Night Mode. thing-type.config.mqtt.rgbandcct.powerFailsToMinimum.label = Dimmed on Power Fail -thing-type.config.mqtt.rgbandcct.powerFailsToMinimum.description = If lights loose power, the lights will turn on to the minimum brightness. +thing-type.config.mqtt.rgbandcct.powerFailsToMinimum.description = If lights lose power, the lights will turn on to the minimum brightness. thing-type.config.mqtt.rgbandcct.whiteHue.label = White Hue thing-type.config.mqtt.rgbandcct.whiteHue.description = When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS. thing-type.config.mqtt.rgbandcct.whiteSat.label = White Saturation thing-type.config.mqtt.rgbandcct.whiteSat.description = When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS. -thing-type.config.mqtt.rgbandcct.whiteThreshold.label = White Threshold -thing-type.config.mqtt.rgbandcct.whiteThreshold.description = Saturation values at or below this value on a RGBW color control will trigger the white mode. -1 will disable this feature. thing-type.config.mqtt.rgbw.oneTriggersNightMode.label = 1% Triggers Night Mode thing-type.config.mqtt.rgbw.oneTriggersNightMode.description = 1% on a slider will trigger the Night Mode. thing-type.config.mqtt.rgbw.powerFailsToMinimum.label = Dimmed on Power Fail -thing-type.config.mqtt.rgbw.powerFailsToMinimum.description = If lights loose power, the lights will turn on to the minimum brightness. +thing-type.config.mqtt.rgbw.powerFailsToMinimum.description = If lights lose power, the lights will turn on to the minimum brightness. thing-type.config.mqtt.rgbw.whiteHue.label = White Hue thing-type.config.mqtt.rgbw.whiteHue.description = When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS. thing-type.config.mqtt.rgbw.whiteSat.label = White Saturation diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/thing/thing-types.xml index 14fd4892b872c..165320bb1edfd 100644 --- a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/thing/thing-types.xml @@ -9,7 +9,7 @@ - Led globe with full Colour, and both cool and warm whites. + LED globe with full Colour, and both cool and warm whites. Lightbulb @@ -60,7 +60,7 @@ - Led globe with both cool and warm white controls + LED globe with both cool and warm white controls Lightbulb From 740d1a7f8515670247239570a23c9a1fd1c15afe Mon Sep 17 00:00:00 2001 From: M Valla <12682715+mvalla@users.noreply.github.com> Date: Tue, 29 Nov 2022 12:08:22 +0100 Subject: [PATCH 6/9] [openwebnet] Thermo: zone/CU address set correctly during discovery (#13803) Fixes #13761 Signed-off-by: Massimo Valla --- .../org.openhab.binding.openwebnet/README.md | 2 +- .../OpenWebNetDeviceDiscoveryService.java | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.openwebnet/README.md b/bundles/org.openhab.binding.openwebnet/README.md index d03229e9b5a63..8a14103fcd08a 100644 --- a/bundles/org.openhab.binding.openwebnet/README.md +++ b/bundles/org.openhab.binding.openwebnet/README.md @@ -155,7 +155,7 @@ Temperature sensors can be configured defining a `bus_thermo_sensor` Thing with - example sensor `5` of external zone `00` --> `where="500"` - example: slave sensor `3` of zone `2` --> `where="302"` -The (optional) Central Unit can be configured defining a `bus_themo_cu` Thing. +The (optional) Central Unit can be configured defining a `bus_themo_cu` Thing with the `where` configuration parameter (`OpenWebNet Address`) set to `where="0"`. ##### Central Unit integration missing points diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/discovery/OpenWebNetDeviceDiscoveryService.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/discovery/OpenWebNetDeviceDiscoveryService.java index aa3ecbb1eccfd..ce6264c0fcb52 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/discovery/OpenWebNetDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/discovery/OpenWebNetDeviceDiscoveryService.java @@ -31,14 +31,15 @@ import org.openwebnet4j.OpenDeviceType; import org.openwebnet4j.message.BaseOpenMessage; import org.openwebnet4j.message.Where; +import org.openwebnet4j.message.WhereThermo; import org.openwebnet4j.message.WhereZigBee; import org.openwebnet4j.message.Who; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * The {@link OpenWebNetDeviceDiscoveryService} is responsible for discovering OpenWebNet devices connected to a - * bridge/gateway + * The {@link OpenWebNetDeviceDiscoveryService} is responsible for discovering + * OpenWebNet devices connected to a bridge/gateway * * @author Massimo Valla - Initial contribution * @author Andrea Conte - Energy management, Thermoregulation @@ -86,14 +87,16 @@ public void abortScan() { } /** - * Create and notify to Inbox a new DiscoveryResult based on WHERE, OpenDeviceType and BaseOpenMessage + * Create and notify to Inbox a new DiscoveryResult based on WHERE, + * OpenDeviceType and BaseOpenMessage * * @param where the discovered device's address (WHERE) * @param deviceType {@link OpenDeviceType} of the discovered device - * @param message the OWN message received that identified the device (optional) + * @param message the OWN message received that identified the device + * (optional) */ public void newDiscoveryResult(Where where, OpenDeviceType deviceType, @Nullable BaseOpenMessage baseMsg) { - logger.info("newDiscoveryResult() WHERE={}, deviceType={}", where, deviceType); + logger.debug("newDiscoveryResult() WHERE={}, deviceType={}", where, deviceType); ThingTypeUID thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_GENERIC_DEVICE; // generic device String thingLabel = OpenWebNetBindingConstants.THING_LABEL_GENERIC_DEVICE; Who deviceWho = Who.UNKNOWN; @@ -210,6 +213,12 @@ public void newDiscoveryResult(Where where, OpenDeviceType deviceType, @Nullable DiscoveryResult discoveryResult = null; String whereConfig = where.value(); + + // remove # from discovered thermo zone or central unit + if (OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_ZONE.equals(thingTypeUID) + || OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_CU.equals(thingTypeUID)) { + whereConfig = "" + ((WhereThermo) where).getZone(); + } if (where instanceof WhereZigBee && WhereZigBee.UNIT_02.equals(((WhereZigBee) where).getUnit())) { logger.debug("UNIT=02 found (WHERE={}) -> will remove previous result if exists", where); thingRemoved(thingUID); // remove previously discovered thing From ac546dee0f29c54952375667027dbec69d2b5603 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 29 Nov 2022 12:26:38 +0100 Subject: [PATCH 7/9] [miio] cloud login issues improvement (#13715) * [miio] cloud login issues improvement * Small change in debug loggin is login issues are expected * Additional trouble shooting steps in readme * Fix duplicate name in device list preventing readme maker running * Catch a json exception in readme maker close #13611 close #7459 * [miio] fix strange/foreign characters in readme Signed-off-by: Marcel Verpaalen --- .../org.openhab.binding.miio/README.base.md | 10 +- bundles/org.openhab.binding.miio/README.md | 81 +- .../miio/internal/cloud/CloudLogin1DTO.java | 122 + .../miio/internal/cloud/MiCloudConnector.java | 12 +- .../resources/OH-INF/i18n/basic.properties | 27 +- .../database/dreame.vacuum.p2029-miot.json | 19 +- .../database/ijai.vacuum.v19-miot.json | 2194 ++++++++--------- .../database/mijia.vacuum.v2-miot.json | 18 +- .../src/main/resources/misc/device_names.json | 1 - .../binding/miio/internal/ReadmeHelper.java | 3 +- 10 files changed, 1307 insertions(+), 1180 deletions(-) create mode 100644 bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/CloudLogin1DTO.java diff --git a/bundles/org.openhab.binding.miio/README.base.md b/bundles/org.openhab.binding.miio/README.base.md index ef881f4440f1d..81b2dda9ce895 100644 --- a/bundles/org.openhab.binding.miio/README.base.md +++ b/bundles/org.openhab.binding.miio/README.base.md @@ -172,7 +172,15 @@ Firmware of the device don't accept commands coming from other subnets. Set the communication in the thing configuration to 'cloud'. _Cloud connectivity is not working_ -The most common problem is a wrong userId/password. Try to fix your userId/password. +The most common problem is a wrong or missing userId/password. Update your Xiaomi cloud userId & password in the [miio binding configuration screen](#binding-configuration). +If the problem persists you can try the following: + +* Xiaomi Account verification might be needed. For some users login by the binding is unsuccessful as account verification is required, but the binding currently has no possibilities to handle this. +In order to pass validation your (openHAB server) ip need to be validated/confirmed. +Browse to [https://account.xiaomi.com/](https://account.xiaomi.com/) and logon to your account. Note: use the same external ip address as your openHAB server, e.g. you may need to disable your VPN. +* If above is not possible or fails, You can try to find in the binding debug logging a `location url`. Try to login using this url (just after it fails) with your browser. +* Several users also reported success by resetting their Xiaomi password. + If it still fails, you're bit out of luck. You may try to restart openHAB (not just the binding) to clean the cookies. As the cloud logon process is still little understood, your only luck might be to enable trace logging and see if you can translate the Chinese error code that it returns. diff --git a/bundles/org.openhab.binding.miio/README.md b/bundles/org.openhab.binding.miio/README.md index 9ecfa668187fd..934edad58a0a7 100644 --- a/bundles/org.openhab.binding.miio/README.md +++ b/bundles/org.openhab.binding.miio/README.md @@ -172,7 +172,15 @@ Firmware of the device don't accept commands coming from other subnets. Set the communication in the thing configuration to 'cloud'. _Cloud connectivity is not working_ -The most common problem is a wrong userId/password. Try to fix your userId/password. +The most common problem is a wrong or missing userId/password. Update your Xiaomi cloud userId & password in the [miio binding configuration screen](#binding-configuration). +If the problem persists you can try the following: + +* Xiaomi Account verification might be needed. For some users login by the binding is unsuccessful as account verification is required, but the binding currently has no possibilities to handle this. +In order to pass validation your (openHAB server) ip need to be validated/confirmed. +Browse to [https://account.xiaomi.com/](https://account.xiaomi.com/) and logon to your account. Note: use the same external ip address as your openHAB server, e.g. you may need to disable your VPN. +* If above is not possible or fails, You can try to find in the binding debug logging a `location url`. Try to login using this url (just after it fails) with your browser. +* Several users also reported success by resetting their Xiaomi password. + If it still fails, you're bit out of luck. You may try to restart openHAB (not just the binding) to clean the cookies. As the cloud logon process is still little understood, your only luck might be to enable trace logging and see if you can translate the Chinese error code that it returns. @@ -347,6 +355,17 @@ Currently the miio binding supports more than 340 different models. | Roborock S7 | miio:vacuum | [roborock.vacuum.a15](#roborock-vacuum-a15) | Yes | | | Roborock S4 Max | miio:vacuum | [roborock.vacuum.a19](#roborock-vacuum-a19) | Yes | | | Roborock T7S Plus | miio:vacuum | [roborock.vacuum.a23](#roborock-vacuum-a23) | Yes | | +| Roborock G10S Pro | miio:vacuum | [roborock.vacuum.a26](#roborock-vacuum-a26) | Yes | | +| Roborock S7 MaxV | miio:vacuum | [roborock.vacuum.a27](#roborock-vacuum-a27) | Yes | | +| Roborock G10 | miio:vacuum | [roborock.vacuum.a29](#roborock-vacuum-a29) | Yes | | +| Roborock G10 | miio:vacuum | [roborock.vacuum.a30](#roborock-vacuum-a30) | Yes | | +| Roborock Q5 | miio:vacuum | [roborock.vacuum.a34](#roborock-vacuum-a34) | Yes | | +| Roborock T8 | miio:vacuum | [roborock.vacuum.a37](#roborock-vacuum-a37) | Yes | | +| Roborock Q7 Max | miio:vacuum | [roborock.vacuum.a38](#roborock-vacuum-a38) | Yes | | +| Roborock Q7 | miio:vacuum | [roborock.vacuum.a40](#roborock-vacuum-a40) | Yes | | +| Roborock G10S | miio:vacuum | [roborock.vacuum.a46](#roborock-vacuum-a46) | Yes | | +| Roborock T8 Plus | miio:vacuum | [roborock.vacuum.a52](#roborock-vacuum-a52) | Yes | | +| Roborock S7 Pro Ultra | miio:vacuum | [roborock.vacuum.a62](#roborock-vacuum-a62) | Yes | | | Xiaowa C1 | miio:vacuum | [roborock.vacuum.c1](#roborock-vacuum-c1) | Yes | | | Roborock Xiaowa E Series Vacuum v2 | miio:unsupported | roborock.vacuum.e2 | No | | | Mi Robot Vacuum 1S | miio:vacuum | [roborock.vacuum.m1s](#roborock-vacuum-m1s) | Yes | | @@ -1259,7 +1278,7 @@ Note, not all the values need to be in the json file, e.g. a subset of the param | Channel | Type | Description | Comment | |----------------------|----------------------|------------------------------------------|------------| -| actions | String | Actions | | +| actions | String | Actions | Value mapping `["vacuum-start-sweep"="Vacuum Start Sweep","vacuum-stop-sweeping"="Vacuum Stop Sweeping","battery-start-charge"="Battery Start Charge","brush-cleaner-reset-brush-life"="Brush Cleaner Reset Brush Life","brush-cleaner-reset-brush-life"="Brush Cleaner Reset Brush Life","filter-reset-filter-life"="Filter Reset Filter Life","vacuum-extend-start-clean"="Vacuum Extend Start Clean","vacuum-extend-stop-clean"="Vacuum Extend Stop Clean","map-map-req"="Map Map Req","map-update-map"="Map Update Map","audio-position"="Audio Position","audio-play-sound"="Audio Play Sound","time-delete-timer"="Time Delete Timer"]` | | status | Number | Robot Cleaner - Status | Value mapping `["1"="Sweeping","2"="Idle","3"="Paused","4"="Error","5"="Go Charging","6"="Charging","7"="Mopping"]` | | fault | Number | Robot Cleaner - Device Fault | | | battery_level | Number:Dimensionless | Battery - Battery Level | | @@ -1701,8 +1720,8 @@ Note, not all the values need to be in the json file, e.g. a subset of the param | Channel | Type | Description | Comment | |----------------------|----------------------|------------------------------------------|------------| -| actions | String | Robot Cleaner - Actions | | -| advanced_actions | String | Robot Cleaner - Advanced Actions | | +| actions | String | Robot Cleaner - Actions | Value mapping `["vacuum-start-sweep"="Start","vacuum-start-sweep-mop"="Start Sweep+Mop","vacuum-start-only-sweep"="Start Sweep","vacuum-start-only-mop"="Start Mop","vacuum-dock"="Dock","vacuum-stop-sweeping"="Pause","vacuum-start-room-sweep"="Start Room Sweep"]` | +| advanced_actions | String | Robot Cleaner - Advanced Actions | Value mapping `["sweep-reset-consumable"="Sweep Reset Consumable","sweep-set-calibration"="Sweep Set Calibration","sweep-set-room-clean"="Sweep Set Room Clean","sweep-set-preference-clean"="Sweep Set Preference Clean","sweep-get-preference-clean"="Sweep Get Preference Clean","sweep-set-preference-type"="Sweep Set Preference Type","sweep-set-go-charging"="Sweep Set Go Charging","sweep-erase-preference"="Sweep Erase Preference","sweep-set-preference-ii"="Sweep Set Preference Ii","sweep-get-preference-ii"="Sweep Get Preference Ii","order-add"="Order Add","order-del"="Order Del","order-get"="Order Get","order-add-ii"="Order Add Ii","order-get-map-order-count"="Order Get Map Order Count","order-add-iii"="Order Add Iii","point-zone-start-point-clean"="Point Zone Start Point Clean","point-zone-pause-point-clean"="Point Zone Pause Point Clean","point-zone-start-zone-clean"="Point Zone Start Zone Clean","point-zone-pause-zone-clean"="Point Zone Pause Zone Clean","point-zone-set-beauty-wall"="Point Zone Set Beauty Wall","point-zone-set-virtual-wall"="Point Zone Set Virtual Wall","point-zone-set-zone-point"="Point Zone Set Zone Point","point-zone-start-point-clean-ii"="Point Zone Start Point Clean Ii","map-get-map-list"="Map Get Map List","map-upload-by-mapid"="Map Upload By Mapid","map-set-cur-map"="Map Set Cur Map","map-del-map"="Map Del Map","map-rename-map"="Map Rename Map","map-upload-by-maptype"="Map Upload By Maptype","map-rename-room"="Map Rename Room","map-arrange-room"="Map Arrange Room","map-split-room"="Map Split Room","map-reset-map"="Map Reset Map","map-build-new-map"="Map Build New Map","map-get-cur-path"="Map Get Cur Path","map-get-map-room-list"="Map Get Map Room List","map-upload-by-mapid-ii"="Map Upload By Mapid Ii","map-upload-by-maptype-ii"="Map Upload By Maptype Ii","map-reset-map-ii"="Map Reset Map Ii","map-build-map-ii"="Map Build Map Ii","map-set-mijia-room-list"="Map Set Mijia Room List","disturb-set-notdisturb"="Disturb Set Notdisturb","language-download-voice"="Language Download Voice","language-get-download-status"="Language Get Download Status"]` | | status | Number | Robot Cleaner - Status | Value mapping `["0"="Sleep","1"="Idle","2"="Paused","3"="Go Charging","4"="Charging","5"="Sweeping","6"="Sweeping and Mopping","7"="Mopping","8"="Upgrading"]` | | fault | Number | Robot Cleaner - Device Fault | | | mode | Number | Robot Cleaner - Mode | Value mapping `["0"="Sweep","1"="Sweep And Mop","2"="Mop"]` | @@ -2013,7 +2032,7 @@ Note, not all the values need to be in the json file, e.g. a subset of the param | total-clean-area | Number:Area | Clean Record - Total Clean Area | | | total-clean-time | Number | Clean Record - Total Clean Time | | | total-clean-count | Number | Clean Record - Total Clean Count | | -| language | Number | Language - Language | Value mapping `["0"="English","1"="简体中文","2"="Español","3"="Русский","4"="Italiano","5"="Français","6"="Deutsch","7"="한국어","8"="Polski"]` | +| language | Number | Language - Language | Value mapping `["0"="English","1"="Chinese","2"="Spanish","3"="Russian","4"="Italian","5"="French","6"="German","7"="Korean","8"="Polish"]` | | not-disturb-switch | Switch | Language - Not Disturb Switch | | | mop-status | Number | Other Status - Mop Status | Value mapping `["0"="Mop Uninstall","1"="Mop Install"]` | @@ -2654,14 +2673,15 @@ Note, not all the values need to be in the json file, e.g. a subset of the param | Channel | Type | Description | Comment | |----------------------|----------------------|------------------------------------------|------------| | power | Switch | Power | | -| powerUsage | Number | Power Consumption | | +| mode | String | Mode | Value mapping `["normal"="Normal","green"="Green"]` | +| powerUsage | Number:Power | Power Consumption | | +| voltage | Number:ElectricPotential | Voltage | | | led | Switch | wifi LED | | -| power_price | Number | power_price | | -| current | Number | Current | | +| power_price | Number | Power Price | | +| power_factor | Number | Power Factor | | +| current | Number:ElectricCurrent | Current | | +| elec_leakage | Number:ElectricCurrent | Electic Leakage | | | temperature | Number:Temperature | Temperature | | -| lp_autooff | Number | Low Power Auto Off | | -| lp_autooff_delay | Number | Low Power Limit Time | | -| lp_threshold | Number | Low Power Threshold | | ### ROIDMI EVE vacuum (roidmi.vacuum.v60) Channels @@ -5243,15 +5263,21 @@ Note, not all the values need to be in the json file, e.g. a subset of the param | Channel | Type | Description | Comment | |----------------------|----------------------|------------------------------------------|------------| | power | Switch | Power | | -| mode | String | Mode | | +| mode | String | Mode | Value mapping `["auto"="Auto","favorite"="Favorite","silent"="Silent","high"="High","medium"="Medium","idle"="Idle","strong"="Strong"]` | | humidity | Number:Dimensionless | Humidity | | | aqi | Number | Air Quality Index | | -| brightness | Dimmer | Brightness | | +| averageaqi | Number | Average Air Quality Index | | | led | Switch | LED Status | | -| act_det | Switch | Air AutoDetect | | | buzzer | Switch | Buzzer Status | | | filtermaxlife | Number | Filter Max Life | | -| filterlive | Number | Filter Life | | +| filterhours | Number:Time | Filter Hours used | | +| usedhours | Number:Time | Run Time | | +| motorspeed | Number | Motor Speed | | +| filterlife | Number | Filter Life | | +| favoritelevel | Number | Favorite Level | Value mapping `["0"="Favorite 0","1"="Favorite 1","2"="Favorite 2","3"="Favorite 3","4"="Favorite 4","5"="Favorite 5","6"="Favorite 6","7"="Favorite 7","8"="Favorite 8","9"="Favorite 9","10"="Favorite 10","11"="Favorite 11","12"="Favorite 13","13"="Favorite 13","14"="Favorite 14","15"="Favorite 15"]` | +| temperature | Number:Temperature | Temperature | | +| purifyvolume | Number:Volume | Purified Volume | | +| childlock | Switch | Child Lock | | ### Mi Air Purifier v2 (zhimi.airpurifier.v2) Channels @@ -8331,14 +8357,15 @@ note: Autogenerated example. Replace the id (powerstrip) in the channel with you ``` Group G_powerstrip "CHINGMI Smart Power Strip v1" Switch power "Power" (G_powerstrip) {channel="miio:basic:powerstrip:power"} -Number powerUsage "Power Consumption" (G_powerstrip) {channel="miio:basic:powerstrip:powerUsage"} +String mode "Mode" (G_powerstrip) {channel="miio:basic:powerstrip:mode"} +Number:Power powerUsage "Power Consumption" (G_powerstrip) {channel="miio:basic:powerstrip:powerUsage"} +Number:ElectricPotential voltage "Voltage" (G_powerstrip) {channel="miio:basic:powerstrip:voltage"} Switch led "wifi LED" (G_powerstrip) {channel="miio:basic:powerstrip:led"} -Number power_price "power_price" (G_powerstrip) {channel="miio:basic:powerstrip:power_price"} -Number current "Current" (G_powerstrip) {channel="miio:basic:powerstrip:current"} +Number power_price "Power Price" (G_powerstrip) {channel="miio:basic:powerstrip:power_price"} +Number power_factor "Power Factor" (G_powerstrip) {channel="miio:basic:powerstrip:power_factor"} +Number:ElectricCurrent current "Current" (G_powerstrip) {channel="miio:basic:powerstrip:current"} +Number:ElectricCurrent elec_leakage "Electic Leakage" (G_powerstrip) {channel="miio:basic:powerstrip:elec_leakage"} Number:Temperature temperature "Temperature" (G_powerstrip) {channel="miio:basic:powerstrip:temperature"} -Number lp_autooff "Low Power Auto Off" (G_powerstrip) {channel="miio:basic:powerstrip:lp_autooff"} -Number lp_autooff_delay "Low Power Limit Time" (G_powerstrip) {channel="miio:basic:powerstrip:lp_autooff_delay"} -Number lp_threshold "Low Power Threshold" (G_powerstrip) {channel="miio:basic:powerstrip:lp_threshold"} ``` ### ROIDMI EVE vacuum (roidmi.vacuum.v60) item file lines @@ -11383,12 +11410,18 @@ Switch power "Power" (G_airpurifier) {channel="miio:basic:airpurifier:power"} String mode "Mode" (G_airpurifier) {channel="miio:basic:airpurifier:mode"} Number:Dimensionless humidity "Humidity" (G_airpurifier) {channel="miio:basic:airpurifier:humidity"} Number aqi "Air Quality Index" (G_airpurifier) {channel="miio:basic:airpurifier:aqi"} -Dimmer brightness "Brightness" (G_airpurifier) {channel="miio:basic:airpurifier:brightness"} +Number averageaqi "Average Air Quality Index" (G_airpurifier) {channel="miio:basic:airpurifier:averageaqi"} Switch led "LED Status" (G_airpurifier) {channel="miio:basic:airpurifier:led"} -Switch act_det "Air AutoDetect" (G_airpurifier) {channel="miio:basic:airpurifier:act_det"} Switch buzzer "Buzzer Status" (G_airpurifier) {channel="miio:basic:airpurifier:buzzer"} Number filtermaxlife "Filter Max Life" (G_airpurifier) {channel="miio:basic:airpurifier:filtermaxlife"} -Number filterlive "Filter Life" (G_airpurifier) {channel="miio:basic:airpurifier:filterlive"} +Number:Time filterhours "Filter Hours used" (G_airpurifier) {channel="miio:basic:airpurifier:filterhours"} +Number:Time usedhours "Run Time" (G_airpurifier) {channel="miio:basic:airpurifier:usedhours"} +Number motorspeed "Motor Speed" (G_airpurifier) {channel="miio:basic:airpurifier:motorspeed"} +Number filterlife "Filter Life" (G_airpurifier) {channel="miio:basic:airpurifier:filterlife"} +Number favoritelevel "Favorite Level" (G_airpurifier) {channel="miio:basic:airpurifier:favoritelevel"} +Number:Temperature temperature "Temperature" (G_airpurifier) {channel="miio:basic:airpurifier:temperature"} +Number:Volume purifyvolume "Purified Volume" (G_airpurifier) {channel="miio:basic:airpurifier:purifyvolume"} +Switch childlock "Child Lock" (G_airpurifier) {channel="miio:basic:airpurifier:childlock"} ``` ### Mi Air Purifier v2 (zhimi.airpurifier.v2) item file lines diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/CloudLogin1DTO.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/CloudLogin1DTO.java new file mode 100644 index 0000000000000..5c05854433664 --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/CloudLogin1DTO.java @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2010-2022 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.miio.internal.cloud; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * This DTO class wraps the login step 1 json structure + * + * @author Marcel Verpaalen - Initial contribution + */ +public class CloudLogin1DTO { + @SerializedName("serviceParam") + @Expose + private String serviceParam; + @SerializedName("qs") + @Expose + private String qs; + @SerializedName("code") + @Expose + private Integer code; + @SerializedName("description") + @Expose + private String description; + @SerializedName("securityStatus") + @Expose + private Integer securityStatus; + @SerializedName("_sign") + @Expose + private String sign; + @SerializedName("sid") + @Expose + private String sid; + @SerializedName("result") + @Expose + private String result; + @SerializedName("captchaUrl") + @Expose + private String captchaUrl; + @SerializedName("callback") + @Expose + private String callback; + @SerializedName("location") + @Expose + private String location; + @SerializedName("pwd") + @Expose + private Integer pwd; + @SerializedName("child") + @Expose + private Integer child; + @SerializedName("desc") + @Expose + private String desc; + + public String getServiceParam() { + return serviceParam; + } + + public String getQs() { + return qs; + } + + public Integer getCode() { + return code; + } + + public String getDescription() { + return description; + } + + public Integer getSecurityStatus() { + return securityStatus; + } + + public String getSign() { + return sign; + } + + public String getSid() { + return sid; + } + + public String getResult() { + return result; + } + + public String getCaptchaUrl() { + return captchaUrl; + } + + public String getCallback() { + return callback; + } + + public String getLocation() { + return location; + } + + public Integer getPwd() { + return pwd; + } + + public Integer getChild() { + return child; + } + + public String getDesc() { + return desc; + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/MiCloudConnector.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/MiCloudConnector.java index 9f3bc649101c7..947067c47832d 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/MiCloudConnector.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/MiCloudConnector.java @@ -411,8 +411,9 @@ private String loginStep1() throws InterruptedException, TimeoutException, Execu logger.trace("Xiaomi Login step 1 response = {}", responseStep1); try { JsonElement resp = JsonParser.parseString(parseJson(content)); - if (resp.isJsonObject() && resp.getAsJsonObject().has("_sign")) { - String sign = resp.getAsJsonObject().get("_sign").getAsString(); + CloudLogin1DTO jsonResp = GSON.fromJson(resp, CloudLogin1DTO.class); + final String sign = jsonResp.getSign(); + if (sign != null && !sign.isBlank()) { logger.trace("Xiaomi Login step 1 sign = {}", sign); return sign; } else { @@ -472,6 +473,13 @@ private String loginStep2(String sign) throws MiIoCryptoException, InterruptedEx logger.trace("Xiaomi login passToken = {}", passToken); logger.trace("Xiaomi login location = {}", location); logger.trace("Xiaomi login code = {}", code); + if (0 != jsonResp.getSecurityStatus()) { + logger.debug("Xiaomi Cloud Step2 response: {}", parseJson(content2)); + logger.debug( + "Xiaomi Login code: {} \r\nSecurityStatus: {}\r\nPwd code: {}\r\nLocation logon URL: {}\r\nIn case of login issues check userId/password details are correct.\r\n" + + "If login details are correct, try to logon using browser from the openHAB ip using the browser. Alternatively try to complete logon with above URL.", + jsonResp.getCode(), jsonResp.getSecurityStatus(), jsonResp.getPwd(), jsonResp.getLocation()); + } if (logger.isTraceEnabled()) { dumpCookies(url, false); } diff --git a/bundles/org.openhab.binding.miio/src/main/resources/OH-INF/i18n/basic.properties b/bundles/org.openhab.binding.miio/src/main/resources/OH-INF/i18n/basic.properties index 4d2e6759289cb..bf8cdf41c7799 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/OH-INF/i18n/basic.properties +++ b/bundles/org.openhab.binding.miio/src/main/resources/OH-INF/i18n/basic.properties @@ -161,6 +161,17 @@ thing.roborock.vacuum.a14 = Roborock T7S thing.roborock.vacuum.a15 = Roborock S7 thing.roborock.vacuum.a19 = Roborock S4 Max thing.roborock.vacuum.a23 = Roborock T7S Plus +thing.roborock.vacuum.a26 = Roborock G10S Pro +thing.roborock.vacuum.a27 = Roborock S7 MaxV +thing.roborock.vacuum.a29 = Roborock G10 +thing.roborock.vacuum.a30 = Roborock G10 +thing.roborock.vacuum.a34 = Roborock Q5 +thing.roborock.vacuum.a37 = Roborock T8 +thing.roborock.vacuum.a38 = Roborock Q7 Max +thing.roborock.vacuum.a40 = Roborock Q7 +thing.roborock.vacuum.a46 = Roborock G10S +thing.roborock.vacuum.a52 = Roborock T8 Plus +thing.roborock.vacuum.a62 = Roborock S7 Pro Ultra thing.roborock.vacuum.c1 = Xiaowa C1 thing.roborock.vacuum.e2 = Roborock Xiaowa E Series Vacuum v2 thing.roborock.vacuum.m1s = Mi Robot Vacuum 1S @@ -2773,14 +2784,14 @@ option.mijia.vacuum.v2-miot.fault-7 = Side-brush-error option.mijia.vacuum.v2-miot.fault-8 = Fan-motor-error option.mijia.vacuum.v2-miot.fault-9 = Dustbin-error option.mijia.vacuum.v2-miot.language-0 = English -option.mijia.vacuum.v2-miot.language-1 = 简体中文 -option.mijia.vacuum.v2-miot.language-2 = Español -option.mijia.vacuum.v2-miot.language-3 = Русский -option.mijia.vacuum.v2-miot.language-4 = Italiano -option.mijia.vacuum.v2-miot.language-5 = Français -option.mijia.vacuum.v2-miot.language-6 = Deutsch -option.mijia.vacuum.v2-miot.language-7 = 한국어 -option.mijia.vacuum.v2-miot.language-8 = Polski +option.mijia.vacuum.v2-miot.language-1 = Chinese +option.mijia.vacuum.v2-miot.language-2 = Spanish +option.mijia.vacuum.v2-miot.language-3 = Russian +option.mijia.vacuum.v2-miot.language-4 = Italian +option.mijia.vacuum.v2-miot.language-5 = French +option.mijia.vacuum.v2-miot.language-6 = German +option.mijia.vacuum.v2-miot.language-7 = Korean +option.mijia.vacuum.v2-miot.language-8 = Polish option.mijia.vacuum.v2-miot.mode-1 = Auto-clean option.mijia.vacuum.v2-miot.mode-2 = Spot-clean option.mijia.vacuum.v2-miot.mode-3 = Wallflow-clean diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2029-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2029-miot.json index c581c8424738f..1511178e2d871 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2029-miot.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2029-miot.json @@ -263,7 +263,8 @@ ] } } - ] + ], + "readmeComment": "Value mapping `[\"vacuum-start-sweep\"\u003d\"Vacuum Start Sweep\",\"vacuum-stop-sweeping\"\u003d\"Vacuum Stop Sweeping\",\"battery-start-charge\"\u003d\"Battery Start Charge\",\"brush-cleaner-reset-brush-life\"\u003d\"Brush Cleaner Reset Brush Life\",\"brush-cleaner-reset-brush-life\"\u003d\"Brush Cleaner Reset Brush Life\",\"filter-reset-filter-life\"\u003d\"Filter Reset Filter Life\",\"vacuum-extend-start-clean\"\u003d\"Vacuum Extend Start Clean\",\"vacuum-extend-stop-clean\"\u003d\"Vacuum Extend Stop Clean\",\"map-map-req\"\u003d\"Map Map Req\",\"map-update-map\"\u003d\"Map Update Map\",\"audio-position\"\u003d\"Audio Position\",\"audio-play-sound\"\u003d\"Audio Play Sound\",\"time-delete-timer\"\u003d\"Time Delete Timer\"]`" }, { "property": "status", @@ -307,7 +308,7 @@ }, "refresh": true, "actions": [], - "readmeComment": "Value mapping [\"1\"\u003d\"Sweeping\",\"2\"\u003d\"Idle\",\"3\"\u003d\"Paused\",\"4\"\u003d\"Error\",\"5\"\u003d\"Go Charging\",\"6\"\u003d\"Charging\",\"7\"\u003d\"Mopping\"]" + "readmeComment": "Value mapping `[\"1\"\u003d\"Sweeping\",\"2\"\u003d\"Idle\",\"3\"\u003d\"Paused\",\"4\"\u003d\"Error\",\"5\"\u003d\"Go Charging\",\"6\"\u003d\"Charging\",\"7\"\u003d\"Mopping\"]`" }, { "property": "fault", @@ -370,7 +371,7 @@ }, "refresh": true, "actions": [], - "readmeComment": "Value mapping [\"1\"\u003d\"Charging\",\"2\"\u003d\"Not Charging\",\"5\"\u003d\"Go Charging\"]" + "readmeComment": "Value mapping `[\"1\"\u003d\"Charging\",\"2\"\u003d\"Not Charging\",\"5\"\u003d\"Go Charging\"]`" }, { "property": "brush-left-time", @@ -566,7 +567,7 @@ "parameterType": "STRING" } ], - "readmeComment": "Value mapping [\"0\"\u003d\"Silent\",\"1\"\u003d\"Standard\",\"2\"\u003d\"Strong\",\"3\"\u003d\"Turbo\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Silent\",\"1\"\u003d\"Standard\",\"2\"\u003d\"Strong\",\"3\"\u003d\"Turbo\"]`" }, { "property": "mop-mode", @@ -598,7 +599,7 @@ "parameterType": "STRING" } ], - "readmeComment": "Value mapping [\"1\"\u003d\"Low\",\"2\"\u003d\"Medium\",\"3\"\u003d\"High\"]" + "readmeComment": "Value mapping `[\"1\"\u003d\"Low\",\"2\"\u003d\"Medium\",\"3\"\u003d\"High\"]`" }, { "property": "waterbox-status", @@ -622,7 +623,7 @@ }, "refresh": true, "actions": [], - "readmeComment": "Value mapping [\"0\"\u003d\"Removed\",\"1\"\u003d\"Installed\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Removed\",\"1\"\u003d\"Installed\"]`" }, { "property": "task-status", @@ -682,7 +683,7 @@ "parameterType": "NUMBER" } ], - "readmeComment": "Value mapping [\"0\"\u003d\"Off\",\"1\"\u003d\"On\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Off\",\"1\"\u003d\"On\"]`" }, { "property": "carpet-press", @@ -710,7 +711,7 @@ "parameterType": "NUMBER" } ], - "readmeComment": "Value mapping [\"0\"\u003d\"On\",\"1\"\u003d\"Off\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"On\",\"1\"\u003d\"Off\"]`" }, { "property": "serial-number", @@ -894,7 +895,7 @@ "parameterType": "NUMBER" } ], - "readmeComment": "Value mapping [\"0\"\u003d\"Close\",\"1\"\u003d\"Open\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Close\",\"1\"\u003d\"Open\"]`" }, { "property": "mult-map-info", diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/ijai.vacuum.v19-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/ijai.vacuum.v19-miot.json index 679dceb8ae5f9..741db7239c3d4 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/ijai.vacuum.v19-miot.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/ijai.vacuum.v19-miot.json @@ -1,160 +1,161 @@ { - "deviceMapping":{ - "id":[ + "deviceMapping": { + "id": [ "ijai.vacuum.v19" ], - "propertyMethod":"get_properties", - "maxProperties":4, - "channels":[ + "propertyMethod": "get_properties", + "maxProperties": 4, + "channels": [ { - "property":"", - "friendlyName":"Robot Cleaner - Actions", - "channel":"actions", - "type":"String", - "stateDescription":{ - "options":[ + "property": "", + "friendlyName": "Robot Cleaner - Actions", + "channel": "actions", + "type": "String", + "stateDescription": { + "options": [ { - "value":"vacuum-start-sweep", - "label":"Start" + "value": "vacuum-start-sweep", + "label": "Start" }, { - "value":"vacuum-start-sweep-mop", - "label":"Start Sweep+Mop" + "value": "vacuum-start-sweep-mop", + "label": "Start Sweep+Mop" }, { - "value":"vacuum-start-only-sweep", - "label":"Start Sweep" + "value": "vacuum-start-only-sweep", + "label": "Start Sweep" }, { - "value":"vacuum-start-only-mop", - "label":"Start Mop" + "value": "vacuum-start-only-mop", + "label": "Start Mop" }, { - "value":"vacuum-dock", - "label":"Dock" + "value": "vacuum-dock", + "label": "Dock" }, { - "value":"vacuum-stop-sweeping", - "label":"Pause" + "value": "vacuum-stop-sweeping", + "label": "Pause" }, { - "value":"vacuum-start-room-sweep", - "label":"Start Room Sweep" + "value": "vacuum-start-room-sweep", + "label": "Start Room Sweep" } ] }, - "refresh":false, - "actions":[ - { - "command":"action", - "parameterType":"EMPTY", - "siid":2, - "aiid":1, - "condition":{ - "name":"matchValue", - "parameters":[ + "refresh": false, + "actions": [ + { + "command": "action", + "parameterType": "EMPTY", + "siid": 2, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ { - "matchValue":"vacuum-start-sweep" + "matchValue": "vacuum-start-sweep" } ] } }, { - "command":"action", - "parameterType":"EMPTY", - "siid":2, - "aiid":2, - "condition":{ - "name":"matchValue", - "parameters":[ + "command": "action", + "parameterType": "EMPTY", + "siid": 2, + "aiid": 2, + "condition": { + "name": "matchValue", + "parameters": [ { - "matchValue":"vacuum-stop-sweeping" + "matchValue": "vacuum-stop-sweeping" } ] } }, { - "command":"action", - "parameterType":"EMPTY", - "siid":2, - "aiid":3, - "condition":{ - "name":"matchValue", - "parameters":[ + "command": "action", + "parameterType": "EMPTY", + "siid": 2, + "aiid": 3, + "condition": { + "name": "matchValue", + "parameters": [ { - "matchValue":"vacuum-start-only-sweep" + "matchValue": "vacuum-start-only-sweep" } ] } }, { - "command":"action", - "parameterType":"EMPTY", - "siid":2, - "aiid":5, - "condition":{ - "name":"matchValue", - "parameters":[ + "command": "action", + "parameterType": "EMPTY", + "siid": 2, + "aiid": 5, + "condition": { + "name": "matchValue", + "parameters": [ { - "matchValue":"vacuum-start-sweep-mop" + "matchValue": "vacuum-start-sweep-mop" } ] } }, { - "command":"action", - "parameterType":"EMPTY", - "siid":2, - "aiid":6, - "condition":{ - "name":"matchValue", - "parameters":[ + "command": "action", + "parameterType": "EMPTY", + "siid": 2, + "aiid": 6, + "condition": { + "name": "matchValue", + "parameters": [ { - "matchValue":"vacuum-start-only-mop" + "matchValue": "vacuum-start-only-mop" } ] } }, { - "command":"action", - "parameterType":"NUMBER", - "parameters":[ + "command": "action", + "parameterType": "NUMBER", + "parameters": [ 10.0 ], - "siid":2, - "aiid":7, - "condition":{ - "name":"matchValue", - "parameters":[ + "siid": 2, + "aiid": 7, + "condition": { + "name": "matchValue", + "parameters": [ { - "matchValue":"vacuum-start-room-sweep" + "matchValue": "vacuum-start-room-sweep" } ] } }, { - "command":"action", - "parameterType":"EMPTY", - "siid":3, - "aiid":1, - "condition":{ - "name":"matchValue", - "parameters":[ + "command": "action", + "parameterType": "EMPTY", + "siid": 3, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ { - "matchValue":"vacuum-dock" + "matchValue": "vacuum-dock" } ] } } - ] + ], + "readmeComment": "Value mapping `[\"vacuum-start-sweep\"\u003d\"Start\",\"vacuum-start-sweep-mop\"\u003d\"Start Sweep+Mop\",\"vacuum-start-only-sweep\"\u003d\"Start Sweep\",\"vacuum-start-only-mop\"\u003d\"Start Mop\",\"vacuum-dock\"\u003d\"Dock\",\"vacuum-stop-sweeping\"\u003d\"Pause\",\"vacuum-start-room-sweep\"\u003d\"Start Room Sweep\"]`" }, { - "property":"", - "friendlyName":"Robot Cleaner - Advanced Actions", - "channel":"advanced_actions", - "type":"String", - "stateDescription":{ - "options":[ + "property": "", + "friendlyName": "Robot Cleaner - Advanced Actions", + "channel": "advanced_actions", + "type": "String", + "stateDescription": { + "options": [ { "value": "sweep-reset-consumable", "label": "Sweep Reset Consumable" @@ -1134,213 +1135,208 @@ ] } } - ] + ], + "readmeComment": "Value mapping `[\"sweep-reset-consumable\"\u003d\"Sweep Reset Consumable\",\"sweep-set-calibration\"\u003d\"Sweep Set Calibration\",\"sweep-set-room-clean\"\u003d\"Sweep Set Room Clean\",\"sweep-set-preference-clean\"\u003d\"Sweep Set Preference Clean\",\"sweep-get-preference-clean\"\u003d\"Sweep Get Preference Clean\",\"sweep-set-preference-type\"\u003d\"Sweep Set Preference Type\",\"sweep-set-go-charging\"\u003d\"Sweep Set Go Charging\",\"sweep-erase-preference\"\u003d\"Sweep Erase Preference\",\"sweep-set-preference-ii\"\u003d\"Sweep Set Preference Ii\",\"sweep-get-preference-ii\"\u003d\"Sweep Get Preference Ii\",\"order-add\"\u003d\"Order Add\",\"order-del\"\u003d\"Order Del\",\"order-get\"\u003d\"Order Get\",\"order-add-ii\"\u003d\"Order Add Ii\",\"order-get-map-order-count\"\u003d\"Order Get Map Order Count\",\"order-add-iii\"\u003d\"Order Add Iii\",\"point-zone-start-point-clean\"\u003d\"Point Zone Start Point Clean\",\"point-zone-pause-point-clean\"\u003d\"Point Zone Pause Point Clean\",\"point-zone-start-zone-clean\"\u003d\"Point Zone Start Zone Clean\",\"point-zone-pause-zone-clean\"\u003d\"Point Zone Pause Zone Clean\",\"point-zone-set-beauty-wall\"\u003d\"Point Zone Set Beauty Wall\",\"point-zone-set-virtual-wall\"\u003d\"Point Zone Set Virtual Wall\",\"point-zone-set-zone-point\"\u003d\"Point Zone Set Zone Point\",\"point-zone-start-point-clean-ii\"\u003d\"Point Zone Start Point Clean Ii\",\"map-get-map-list\"\u003d\"Map Get Map List\",\"map-upload-by-mapid\"\u003d\"Map Upload By Mapid\",\"map-set-cur-map\"\u003d\"Map Set Cur Map\",\"map-del-map\"\u003d\"Map Del Map\",\"map-rename-map\"\u003d\"Map Rename Map\",\"map-upload-by-maptype\"\u003d\"Map Upload By Maptype\",\"map-rename-room\"\u003d\"Map Rename Room\",\"map-arrange-room\"\u003d\"Map Arrange Room\",\"map-split-room\"\u003d\"Map Split Room\",\"map-reset-map\"\u003d\"Map Reset Map\",\"map-build-new-map\"\u003d\"Map Build New Map\",\"map-get-cur-path\"\u003d\"Map Get Cur Path\",\"map-get-map-room-list\"\u003d\"Map Get Map Room List\",\"map-upload-by-mapid-ii\"\u003d\"Map Upload By Mapid Ii\",\"map-upload-by-maptype-ii\"\u003d\"Map Upload By Maptype Ii\",\"map-reset-map-ii\"\u003d\"Map Reset Map Ii\",\"map-build-map-ii\"\u003d\"Map Build Map Ii\",\"map-set-mijia-room-list\"\u003d\"Map Set Mijia Room List\",\"disturb-set-notdisturb\"\u003d\"Disturb Set Notdisturb\",\"language-download-voice\"\u003d\"Language Download Voice\",\"language-get-download-status\"\u003d\"Language Get Download Status\"]`" }, { - "property":"status", - "siid":2, - "piid":1, - "friendlyName":"Robot Cleaner - Status", - "channel":"status", - "type":"Number", - "stateDescription":{ - "readOnly":true, - "options":[ + "property": "status", + "siid": 2, + "piid": 1, + "friendlyName": "Robot Cleaner - Status", + "channel": "status", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ { - "value":"0", - "label":"Sleep" + "value": "0", + "label": "Sleep" }, { - "value":"1", - "label":"Idle" + "value": "1", + "label": "Idle" }, { - "value":"2", - "label":"Paused" + "value": "2", + "label": "Paused" }, { - "value":"3", - "label":"Go Charging" + "value": "3", + "label": "Go Charging" }, { - "value":"4", - "label":"Charging" + "value": "4", + "label": "Charging" }, { - "value":"5", - "label":"Sweeping" + "value": "5", + "label": "Sweeping" }, { - "value":"6", - "label":"Sweeping and Mopping" + "value": "6", + "label": "Sweeping and Mopping" }, { - "value":"7", - "label":"Mopping" + "value": "7", + "label": "Mopping" }, { - "value":"8", - "label":"Upgrading" + "value": "8", + "label": "Upgrading" } ] }, - "refresh":true, - "actions":[ - - ], - "readmeComment":"Value mapping [\"0\"\u003d\"Sleep\",\"1\"\u003d\"Idle\",\"2\"\u003d\"Paused\",\"3\"\u003d\"Go Charging\",\"4\"\u003d\"Charging\",\"5\"\u003d\"Sweeping\",\"6\"\u003d\"Sweeping and Mopping\",\"7\"\u003d\"Mopping\",\"8\"\u003d\"Upgrading\"]" - }, - { - "property":"fault", - "siid":2, - "piid":2, - "friendlyName":"Robot Cleaner - Device Fault", - "channel":"fault", - "type":"Number", - "stateDescription":{ - "minimum":0, - "maximum":3000, - "step":1, - "pattern":"%.0f", - "readOnly":true + "refresh": true, + "actions": [], + "readmeComment": "Value mapping `[\"0\"\u003d\"Sleep\",\"1\"\u003d\"Idle\",\"2\"\u003d\"Paused\",\"3\"\u003d\"Go Charging\",\"4\"\u003d\"Charging\",\"5\"\u003d\"Sweeping\",\"6\"\u003d\"Sweeping and Mopping\",\"7\"\u003d\"Mopping\",\"8\"\u003d\"Upgrading\"]`" + }, + { + "property": "fault", + "siid": 2, + "piid": 2, + "friendlyName": "Robot Cleaner - Device Fault", + "channel": "fault", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 3000, + "step": 1, + "pattern": "%.0f", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] + "refresh": true, + "actions": [] }, { - "property":"mode", - "siid":2, - "piid":4, - "friendlyName":"Robot Cleaner - Mode", - "channel":"mode", - "type":"Number", - "stateDescription":{ - "options":[ + "property": "mode", + "siid": 2, + "piid": 4, + "friendlyName": "Robot Cleaner - Mode", + "channel": "mode", + "type": "Number", + "stateDescription": { + "options": [ { - "value":"0", - "label":"Sweep" + "value": "0", + "label": "Sweep" }, { - "value":"1", - "label":"Sweep And Mop" + "value": "1", + "label": "Sweep And Mop" }, { - "value":"2", - "label":"Mop" + "value": "2", + "label": "Mop" } ] }, - "refresh":true, - "actions":[ + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ], - "readmeComment":"Value mapping [\"0\"\u003d\"Sweep\",\"1\"\u003d\"Sweep And Mop\",\"2\"\u003d\"Mop\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Sweep\",\"1\"\u003d\"Sweep And Mop\",\"2\"\u003d\"Mop\"]`" }, { - "property":"sweep-type", - "siid":2, - "piid":8, - "friendlyName":"Robot Cleaner - Sweep Type", - "channel":"sweep_type", - "type":"Number", - "stateDescription":{ - "options":[ + "property": "sweep-type", + "siid": 2, + "piid": 8, + "friendlyName": "Robot Cleaner - Sweep Type", + "channel": "sweep_type", + "type": "Number", + "stateDescription": { + "options": [ { - "value":"0", - "label":"Global" + "value": "0", + "label": "Global" }, { - "value":"1", - "label":"Mop" + "value": "1", + "label": "Mop" }, { - "value":"2", - "label":"Edge" + "value": "2", + "label": "Edge" }, { - "value":"3", - "label":"Area" + "value": "3", + "label": "Area" }, { - "value":"4", - "label":"Point" + "value": "4", + "label": "Point" }, { - "value":"5", - "label":"Remote" + "value": "5", + "label": "Remote" }, { - "value":"6", - "label":"Explore" + "value": "6", + "label": "Explore" }, { - "value":"7", - "label":"Room" + "value": "7", + "label": "Room" }, { - "value":"8", - "label":"Floor" + "value": "8", + "label": "Floor" } ] }, - "refresh":true, - "actions":[ + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ], - "readmeComment":"Value mapping [\"0\"\u003d\"Global\",\"1\"\u003d\"Mop\",\"2\"\u003d\"Edge\",\"3\"\u003d\"Area\",\"4\"\u003d\"Point\",\"5\"\u003d\"Remote\",\"6\"\u003d\"Explore\",\"7\"\u003d\"Room\",\"8\"\u003d\"Floor\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Global\",\"1\"\u003d\"Mop\",\"2\"\u003d\"Edge\",\"3\"\u003d\"Area\",\"4\"\u003d\"Point\",\"5\"\u003d\"Remote\",\"6\"\u003d\"Explore\",\"7\"\u003d\"Room\",\"8\"\u003d\"Floor\"]`" }, { - "property":"on", - "siid":2, - "piid":9, - "friendlyName":"Robot Cleaner - Switch Status", - "channel":"on", - "type":"String", - "refresh":true, - "actions":[ + "property": "on", + "siid": 2, + "piid": 9, + "friendlyName": "Robot Cleaner - Switch Status", + "channel": "on", + "type": "String", + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"STRING" + "command": "set_properties", + "parameterType": "STRING" } ] }, { - "property":"battery-level", - "siid":3, - "piid":1, - "friendlyName":"Robot Cleaner - Battery Level", - "channel":"battery_level", - "type":"Number:Dimensionless", - "unit":"percentage", - "stateDescription":{ - "minimum":0, - "maximum":100, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "property": "battery-level", + "siid": 3, + "piid": 1, + "friendlyName": "Robot Cleaner - Battery Level", + "channel": "battery_level", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] + "refresh": true, + "actions": [] }, { - "property":"alarm", - "siid":4, - "piid":1, - "friendlyName":"Robot Cleaner - Locate", - "channel":"alarm", - "type":"Switch", - "refresh":true, - "actions":[ + "property": "alarm", + "siid": 4, + "piid": 1, + "friendlyName": "Robot Cleaner - Locate", + "channel": "alarm", + "type": "Switch", + "refresh": true, + "actions": [ { "command": "set_properties", "parameterType": "ONOFFBOOL" @@ -1348,1204 +1344,1152 @@ ] }, { - "property":"volume", - "siid":4, - "piid":2, - "friendlyName":"Robot Cleaner - Locate Volume", - "channel":"volume", - "type":"Number:Dimensionless", - "stateDescription":{ - "minimum":0, - "maximum":10, - "step":1, - "pattern":"%.0f" + "property": "volume", + "siid": 4, + "piid": 2, + "friendlyName": "Robot Cleaner - Locate Volume", + "channel": "volume", + "type": "Number:Dimensionless", + "stateDescription": { + "minimum": 0, + "maximum": 10, + "step": 1, + "pattern": "%.0f" }, - "refresh":true, - "actions":[ + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ] }, { - "property":"repeat-state", - "siid":7, - "piid":1, - "friendlyName":"Sweep - Repeat State", - "channel":"repeat_state", - "type":"Switch", - "refresh":true, - "actions":[ + "property": "repeat-state", + "siid": 7, + "piid": 1, + "friendlyName": "Sweep - Repeat State", + "channel": "repeat_state", + "type": "Switch", + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"door-state", - "siid":7, - "piid":3, - "friendlyName":"Sweep - Door State", - "channel":"door_state", - "type":"Number", - "stateDescription":{ - "readOnly":true, - "options":[ + "property": "door-state", + "siid": 7, + "piid": 3, + "friendlyName": "Sweep - Door State", + "channel": "door_state", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ { - "value":"0", - "label":"None" + "value": "0", + "label": "None" }, { - "value":"1", - "label":"DustBox" + "value": "1", + "label": "DustBox" }, { - "value":"2", - "label":"WaterBox" + "value": "2", + "label": "WaterBox" }, { - "value":"3", - "label":"TwoInOne" + "value": "3", + "label": "TwoInOne" } ] }, - "refresh":true, - "actions":[ - - ], - "readmeComment":"Value mapping [\"0\"\u003d\"None\",\"1\"\u003d\"DustBox\",\"2\"\u003d\"WaterBox\",\"3\"\u003d\"TwoInOne\"]" - }, - { - "property":"cloth-state", - "siid":7, - "piid":4, - "friendlyName":"Sweep - Cloth State", - "channel":"cloth_state", - "type":"Contact", - "stateDescription":{ - "readOnly":true + "refresh": true, + "actions": [], + "readmeComment": "Value mapping `[\"0\"\u003d\"None\",\"1\"\u003d\"DustBox\",\"2\"\u003d\"WaterBox\",\"3\"\u003d\"TwoInOne\"]`" + }, + { + "property": "cloth-state", + "siid": 7, + "piid": 4, + "friendlyName": "Sweep - Cloth State", + "channel": "cloth_state", + "type": "Contact", + "stateDescription": { + "readOnly": true }, - "refresh":true, - "actions":[ - - ] + "refresh": true, + "actions": [] }, { - "property":"suction-state", - "siid":7, - "piid":5, - "friendlyName":"Robot Cleaner - Power", - "channel":"suction_state", - "type":"Number", - "stateDescription":{ - "options":[ + "property": "suction-state", + "siid": 7, + "piid": 5, + "friendlyName": "Robot Cleaner - Power", + "channel": "suction_state", + "type": "Number", + "stateDescription": { + "options": [ { - "value":"0", - "label":"Slient" + "value": "0", + "label": "Slient" }, { - "value":"1", - "label":"Standard" + "value": "1", + "label": "Standard" }, { - "value":"2", - "label":"Medium" + "value": "2", + "label": "Medium" }, { - "value":"3", - "label":"Turbo" + "value": "3", + "label": "Turbo" } ] }, - "refresh":true, - "actions":[ + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ], - "readmeComment":"Value mapping [\"0\"\u003d\"Slient\",\"1\"\u003d\"Standard\",\"2\"\u003d\"Medium\",\"3\"\u003d\"Turbo\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Slient\",\"1\"\u003d\"Standard\",\"2\"\u003d\"Medium\",\"3\"\u003d\"Turbo\"]`" }, { - "property":"water-state", - "siid":7, - "piid":6, - "friendlyName":"Sweep - Water State", - "channel":"water_state", - "type":"Number", - "stateDescription":{ - "options":[ + "property": "water-state", + "siid": 7, + "piid": 6, + "friendlyName": "Sweep - Water State", + "channel": "water_state", + "type": "Number", + "stateDescription": { + "options": [ { - "value":"0", - "label":"Low" + "value": "0", + "label": "Low" }, { - "value":"1", - "label":"Mid" + "value": "1", + "label": "Mid" }, { - "value":"2", - "label":"High" + "value": "2", + "label": "High" } ] }, - "refresh":true, - "actions":[ + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ], - "readmeComment":"Value mapping [\"0\"\u003d\"Low\",\"1\"\u003d\"Mid\",\"2\"\u003d\"High\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Low\",\"1\"\u003d\"Mid\",\"2\"\u003d\"High\"]`" }, { - "property":"mop-route", - "siid":7, - "piid":7, - "friendlyName":"Sweep - Mop Route", - "channel":"mop_route", - "type":"Number", - "stateDescription":{ - "options":[ + "property": "mop-route", + "siid": 7, + "piid": 7, + "friendlyName": "Sweep - Mop Route", + "channel": "mop_route", + "type": "Number", + "stateDescription": { + "options": [ { - "value":"0", - "label":"S" + "value": "0", + "label": "S" }, { - "value":"1", - "label":"Y" + "value": "1", + "label": "Y" } ] }, - "refresh":true, - "actions":[ + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ], - "readmeComment":"Value mapping [\"0\"\u003d\"S\",\"1\"\u003d\"Y\"]" - }, - { - "property":"side-brush-life", - "siid":7, - "piid":8, - "friendlyName":"Sweep - Side Brush Life", - "channel":"side_brush_life", - "type":"Number:Dimensionless", - "unit":"percentage", - "stateDescription":{ - "minimum":0, - "maximum":100, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "readmeComment": "Value mapping `[\"0\"\u003d\"S\",\"1\"\u003d\"Y\"]`" + }, + { + "property": "side-brush-life", + "siid": 7, + "piid": 8, + "friendlyName": "Sweep - Side Brush Life", + "channel": "side_brush_life", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"side-brush-hours", - "siid":7, - "piid":9, - "friendlyName":"Sweep - Side Brush Hours", - "channel":"side_brush_hours", - "type":"Number:Time", - "unit":"hours", - "stateDescription":{ - "minimum":0, - "maximum":180, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "side-brush-hours", + "siid": 7, + "piid": 9, + "friendlyName": "Sweep - Side Brush Hours", + "channel": "side_brush_hours", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 180, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"main-brush-life", - "siid":7, - "piid":10, - "friendlyName":"Sweep - Main Brush Life", - "channel":"main_brush_life", - "type":"Number:Dimensionless", - "unit":"percentage", - "stateDescription":{ - "minimum":0, - "maximum":100, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "main-brush-life", + "siid": 7, + "piid": 10, + "friendlyName": "Sweep - Main Brush Life", + "channel": "main_brush_life", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"main-brush-hours", - "siid":7, - "piid":11, - "friendlyName":"Sweep - Main Brush Hours", - "channel":"main_brush_hours", - "type":"Number:Time", - "unit":"hours", - "stateDescription":{ - "minimum":0, - "maximum":360, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "main-brush-hours", + "siid": 7, + "piid": 11, + "friendlyName": "Sweep - Main Brush Hours", + "channel": "main_brush_hours", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 360, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"hypa-life", - "siid":7, - "piid":12, - "friendlyName":"Sweep - Hypa Life", - "channel":"hypa_life", - "type":"Number:Dimensionless", - "unit":"percentage", - "stateDescription":{ - "minimum":0, - "maximum":100, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "hypa-life", + "siid": 7, + "piid": 12, + "friendlyName": "Sweep - Hypa Life", + "channel": "hypa_life", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"hypa-hours", - "siid":7, - "piid":13, - "friendlyName":"Sweep - Hypa Hours", - "channel":"hypa_hours", - "type":"Number:Time", - "unit":"hours", - "stateDescription":{ - "minimum":0, - "maximum":180, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "hypa-hours", + "siid": 7, + "piid": 13, + "friendlyName": "Sweep - Hypa Hours", + "channel": "hypa_hours", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 180, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"mop-life", - "siid":7, - "piid":14, - "friendlyName":"Sweep - Mop Life", - "channel":"mop_life", - "type":"Number:Dimensionless", - "unit":"percentage", - "stateDescription":{ - "minimum":0, - "maximum":100, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "mop-life", + "siid": 7, + "piid": 14, + "friendlyName": "Sweep - Mop Life", + "channel": "mop_life", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"mop-hours", - "siid":7, - "piid":15, - "friendlyName":"Sweep - Mop Hours", - "channel":"mop_hours", - "type":"Number:Time", - "unit":"hours", - "stateDescription":{ - "minimum":0, - "maximum":180, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "mop-hours", + "siid": 7, + "piid": 15, + "friendlyName": "Sweep - Mop Hours", + "channel": "mop_hours", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 180, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] + "refresh": true, + "actions": [] }, { - "property":"direction", - "siid":7, - "piid":16, - "friendlyName":"Sweep - Direction", - "channel":"direction", - "type":"Number", - "stateDescription":{ - "options":[ + "property": "direction", + "siid": 7, + "piid": 16, + "friendlyName": "Sweep - Direction", + "channel": "direction", + "type": "Number", + "stateDescription": { + "options": [ { - "value":"1", - "label":"Forward" + "value": "1", + "label": "Forward" }, { - "value":"2", - "label":"Left" + "value": "2", + "label": "Left" }, { - "value":"3", - "label":"Right" + "value": "3", + "label": "Right" }, { - "value":"4", - "label":"Back" + "value": "4", + "label": "Back" }, { - "value":"5", - "label":"Stop" + "value": "5", + "label": "Stop" }, { - "value":"10", - "label":"Exit" + "value": "10", + "label": "Exit" } ] }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ], - "readmeComment":"Value mapping [\"1\"\u003d\"Forward\",\"2\"\u003d\"Left\",\"3\"\u003d\"Right\",\"4\"\u003d\"Back\",\"5\"\u003d\"Stop\",\"10\"\u003d\"Exit\"]" - }, - { - "property":"time-zone", - "siid":7, - "piid":20, - "friendlyName":"Sweep - Time Zone", - "channel":"time_zone", - "type":"Number", - "stateDescription":{ - "step":1, - "pattern":"%.0f" + "readmeComment": "Value mapping `[\"1\"\u003d\"Forward\",\"2\"\u003d\"Left\",\"3\"\u003d\"Right\",\"4\"\u003d\"Back\",\"5\"\u003d\"Stop\",\"10\"\u003d\"Exit\"]`" + }, + { + "property": "time-zone", + "siid": 7, + "piid": 20, + "friendlyName": "Sweep - Time Zone", + "channel": "time_zone", + "type": "Number", + "stateDescription": { + "step": 1, + "pattern": "%.0f" }, - "refresh":true, - "actions":[ + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ] }, { - "property":"cur-lang", - "siid":7, - "piid":21, - "friendlyName":"Sweep - Cur Lang", - "channel":"cur_lang", - "type":"String", - "refresh":true, - "actions":[ + "property": "cur-lang", + "siid": 7, + "piid": 21, + "friendlyName": "Sweep - Cur Lang", + "channel": "cur_lang", + "type": "String", + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"STRING" + "command": "set_properties", + "parameterType": "STRING" } ] }, { - "property":"cleaning-time", - "siid":7, - "piid":22, - "friendlyName":"Sweep - Cleaning Time", - "channel":"cleaning_time", - "type":"Number:Time", - "unit":"minutes", - "stateDescription":{ - "minimum":0, - "maximum":360, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "property": "cleaning-time", + "siid": 7, + "piid": 22, + "friendlyName": "Sweep - Cleaning Time", + "channel": "cleaning_time", + "type": "Number:Time", + "unit": "minutes", + "stateDescription": { + "minimum": 0, + "maximum": 360, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"cleaning-area", - "siid":7, - "piid":23, - "friendlyName":"Sweep - Cleaning Area", - "channel":"cleaning_area", - "type":"Number", - "stateDescription":{ - "minimum":0, - "maximum":1200, - "step":1, - "pattern":"%.0f", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "cleaning-area", + "siid": 7, + "piid": 23, + "friendlyName": "Sweep - Cleaning Area", + "channel": "cleaning_area", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 1200, + "step": 1, + "pattern": "%.0f", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] + "refresh": true, + "actions": [] }, { - "property":"dirt-recognize", - "siid":7, - "piid":35, - "friendlyName":"Sweep - Dirt Recognize", - "channel":"dirt_recognize", - "type":"Switch", - "refresh":true, - "actions":[ + "property": "dirt-recognize", + "siid": 7, + "piid": 35, + "friendlyName": "Sweep - Dirt Recognize", + "channel": "dirt_recognize", + "type": "Switch", + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"pet-recognize", - "siid":7, - "piid":36, - "friendlyName":"Sweep - Pet Recognize", - "channel":"pet_recognize", - "type":"Switch", - "refresh":true, - "actions":[ + "property": "pet-recognize", + "siid": 7, + "piid": 36, + "friendlyName": "Sweep - Pet Recognize", + "channel": "pet_recognize", + "type": "Switch", + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"ai-recognize", - "siid":7, - "piid":42, - "friendlyName":"Sweep - Ai Recognize", - "channel":"ai_recognize", - "type":"Switch", - "refresh":true, - "actions":[ + "property": "ai-recognize", + "siid": 7, + "piid": 42, + "friendlyName": "Sweep - Ai Recognize", + "channel": "ai_recognize", + "type": "Switch", + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"carpet-booster", - "siid":7, - "piid":44, - "friendlyName":"Sweep - Carpet Booster", - "channel":"carpet_booster", - "type":"Switch", - "refresh":true, - "actions":[ + "property": "carpet-booster", + "siid": 7, + "piid": 44, + "friendlyName": "Sweep - Carpet Booster", + "channel": "carpet_booster", + "type": "Switch", + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"multi-prop-vacuum", - "siid":7, - "piid":45, - "friendlyName":"Sweep - Multi Prop Vacuum", - "channel":"multi_prop_vacuum", - "type":"String", - "stateDescription":{ - "readOnly":true + "property": "multi-prop-vacuum", + "siid": 7, + "piid": 45, + "friendlyName": "Sweep - Multi Prop Vacuum", + "channel": "multi_prop_vacuum", + "type": "String", + "stateDescription": { + "readOnly": true }, - "refresh":true, - "actions":[ - - ] + "refresh": true, + "actions": [] }, { - "property":"carpet-avoid", - "siid":7, - "piid":47, - "friendlyName":"Sweep - Carpet Avoid", - "channel":"carpet_avoid", - "type":"Switch", - "refresh":false, - "actions":[ + "property": "carpet-avoid", + "siid": 7, + "piid": 47, + "friendlyName": "Sweep - Carpet Avoid", + "channel": "carpet_avoid", + "type": "Switch", + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"tank-shake", - "siid":7, - "piid":48, - "friendlyName":"Sweep - Tank Shake", - "channel":"tank_shake", - "type":"Switch", - "refresh":true, - "actions":[ + "property": "tank-shake", + "siid": 7, + "piid": 48, + "friendlyName": "Sweep - Tank Shake", + "channel": "tank_shake", + "type": "Switch", + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"shake-shift", - "siid":7, - "piid":50, - "friendlyName":"Sweep - Shake Shift", - "channel":"shake_shift", - "type":"Number", - "stateDescription":{ - "options":[ + "property": "shake-shift", + "siid": 7, + "piid": 50, + "friendlyName": "Sweep - Shake Shift", + "channel": "shake_shift", + "type": "Number", + "stateDescription": { + "options": [ { - "value":"1", - "label":"Low" + "value": "1", + "label": "Low" }, { - "value":"2", - "label":"Mid" + "value": "2", + "label": "Mid" }, { - "value":"3", - "label":"High" + "value": "3", + "label": "High" } ] }, - "refresh":true, - "actions":[ + "refresh": true, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ], - "readmeComment":"Value mapping [\"1\"\u003d\"Low\",\"2\"\u003d\"Mid\",\"3\"\u003d\"High\"]" + "readmeComment": "Value mapping `[\"1\"\u003d\"Low\",\"2\"\u003d\"Mid\",\"3\"\u003d\"High\"]`" }, { - "property":"map-encrypt", - "siid":7, - "piid":55, - "friendlyName":"Sweep - Map Encrypt", - "channel":"map_encrypt", - "type":"Contact", - "stateDescription":{ - "readOnly":true + "property": "map-encrypt", + "siid": 7, + "piid": 55, + "friendlyName": "Sweep - Map Encrypt", + "channel": "map_encrypt", + "type": "Contact", + "stateDescription": { + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"order-id", - "siid":8, - "piid":1, - "friendlyName":"Order - Order Id", - "channel":"order_id", - "type":"Number", - "stateDescription":{ - "minimum":0, - "step":1, - "pattern":"%.0f" + "refresh": true, + "actions": [] + }, + { + "property": "order-id", + "siid": 8, + "piid": 1, + "friendlyName": "Order - Order Id", + "channel": "order_id", + "type": "Number", + "stateDescription": { + "minimum": 0, + "step": 1, + "pattern": "%.0f" }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"STRING" + "command": "set_properties", + "parameterType": "STRING" } ] }, { - "property":"enable", - "siid":8, - "piid":2, - "friendlyName":"Order - Enable", - "channel":"enable", - "type":"Switch", - "refresh":false, - "actions":[ + "property": "enable", + "siid": 8, + "piid": 2, + "friendlyName": "Order - Enable", + "channel": "enable", + "type": "Switch", + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"day", - "siid":8, - "piid":3, - "friendlyName":"Order - Day", - "channel":"day", - "type":"Number:Time", - "unit":"days", - "stateDescription":{ - "minimum":0, - "maximum":255, - "step":1, - "pattern":"%.0f %unit%" + "property": "day", + "siid": 8, + "piid": 3, + "friendlyName": "Order - Day", + "channel": "day", + "type": "Number:Time", + "unit": "days", + "stateDescription": { + "minimum": 0, + "maximum": 255, + "step": 1, + "pattern": "%.0f %unit%" }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ] }, { - "property":"hour", - "siid":8, - "piid":4, - "friendlyName":"Order - Hour", - "channel":"hour", - "type":"Number:Time", - "unit":"hours", - "stateDescription":{ - "minimum":0, - "maximum":23, - "step":1, - "pattern":"%.0f %unit%" + "property": "hour", + "siid": 8, + "piid": 4, + "friendlyName": "Order - Hour", + "channel": "hour", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 23, + "step": 1, + "pattern": "%.0f %unit%" }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ] }, { - "property":"minute", - "siid":8, - "piid":5, - "friendlyName":"Order - Minute", - "channel":"minute", - "type":"Number:Time", - "unit":"minutes", - "stateDescription":{ - "minimum":0, - "maximum":59, - "step":1, - "pattern":"%.0f %unit%" + "property": "minute", + "siid": 8, + "piid": 5, + "friendlyName": "Order - Minute", + "channel": "minute", + "type": "Number:Time", + "unit": "minutes", + "stateDescription": { + "minimum": 0, + "maximum": 59, + "step": 1, + "pattern": "%.0f %unit%" }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ] }, { - "property":"repeat", - "siid":8, - "piid":6, - "friendlyName":"Order - Repeat", - "channel":"repeat", - "type":"Switch", - "refresh":false, - "actions":[ + "property": "repeat", + "siid": 8, + "piid": 6, + "friendlyName": "Order - Repeat", + "channel": "repeat", + "type": "Switch", + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"clean-way", - "siid":8, - "piid":7, - "friendlyName":"Order - Clean Way", - "channel":"clean_way", - "type":"Number", - "stateDescription":{ - "options":[ + "property": "clean-way", + "siid": 8, + "piid": 7, + "friendlyName": "Order - Clean Way", + "channel": "clean_way", + "type": "Number", + "stateDescription": { + "options": [ { - "value":"0", - "label":"Sweep" + "value": "0", + "label": "Sweep" }, { - "value":"1", - "label":"Sweep Mop" + "value": "1", + "label": "Sweep Mop" }, { - "value":"2", - "label":"Mop" + "value": "2", + "label": "Mop" } ] }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ], - "readmeComment":"Value mapping [\"0\"\u003d\"Sweep\",\"1\"\u003d\"Sweep Mop\",\"2\"\u003d\"Mop\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Sweep\",\"1\"\u003d\"Sweep Mop\",\"2\"\u003d\"Mop\"]`" }, { - "property":"suction", - "siid":8, - "piid":8, - "friendlyName":"Order - Suction", - "channel":"suction", - "type":"Number", - "stateDescription":{ - "options":[ + "property": "suction", + "siid": 8, + "piid": 8, + "friendlyName": "Order - Suction", + "channel": "suction", + "type": "Number", + "stateDescription": { + "options": [ { - "value":"0", - "label":"Slient" + "value": "0", + "label": "Slient" }, { - "value":"1", - "label":"Normal" + "value": "1", + "label": "Normal" }, { - "value":"2", - "label":"Medium" + "value": "2", + "label": "Medium" }, { - "value":"3", - "label":"Turbo" + "value": "3", + "label": "Turbo" } ] }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ], - "readmeComment":"Value mapping [\"0\"\u003d\"Slient\",\"1\"\u003d\"Normal\",\"2\"\u003d\"Medium\",\"3\"\u003d\"Turbo\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Slient\",\"1\"\u003d\"Normal\",\"2\"\u003d\"Medium\",\"3\"\u003d\"Turbo\"]`" }, { - "property":"water", - "siid":8, - "piid":9, - "friendlyName":"Order - Water", - "channel":"water", - "type":"Number", - "stateDescription":{ - "options":[ + "property": "water", + "siid": 8, + "piid": 9, + "friendlyName": "Order - Water", + "channel": "water", + "type": "Number", + "stateDescription": { + "options": [ { - "value":"0", - "label":"Low" + "value": "0", + "label": "Low" }, { - "value":"1", - "label":"Mid" + "value": "1", + "label": "Mid" }, { - "value":"2", - "label":"High" + "value": "2", + "label": "High" } ] }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ], - "readmeComment":"Value mapping [\"0\"\u003d\"Low\",\"1\"\u003d\"Mid\",\"2\"\u003d\"High\"]" + "readmeComment": "Value mapping `[\"0\"\u003d\"Low\",\"1\"\u003d\"Mid\",\"2\"\u003d\"High\"]`" }, { - "property":"twice-clean", - "siid":8, - "piid":10, - "friendlyName":"Order - Twice Clean", - "channel":"twice_clean", - "type":"Switch", - "refresh":false, - "actions":[ + "property": "twice-clean", + "siid": 8, + "piid": 10, + "friendlyName": "Order - Twice Clean", + "channel": "twice_clean", + "type": "Switch", + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"mapid", - "siid":8, - "piid":11, - "friendlyName":"Order - Mapid", - "channel":"mapid", - "type":"Number", - "stateDescription":{ - "minimum":0, - "step":1, - "pattern":"%.0f" + "property": "mapid", + "siid": 8, + "piid": 11, + "friendlyName": "Order - Mapid", + "channel": "mapid", + "type": "Number", + "stateDescription": { + "minimum": 0, + "step": 1, + "pattern": "%.0f" }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"STRING" + "command": "set_properties", + "parameterType": "STRING" } ] }, { - "property":"room-count", - "siid":8, - "piid":12, - "friendlyName":"Order - Room Count", - "channel":"room_count", - "type":"Number", - "stateDescription":{ - "minimum":0, - "maximum":64, - "step":1, - "pattern":"%.0f" + "property": "room-count", + "siid": 8, + "piid": 12, + "friendlyName": "Order - Room Count", + "channel": "room_count", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 64, + "step": 1, + "pattern": "%.0f" }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ] }, { - "property":"room-data", - "siid":8, - "piid":13, - "friendlyName":"Order - Room Data", - "channel":"room_data", - "type":"String", - "refresh":false, - "actions":[ + "property": "room-data", + "siid": 8, + "piid": 13, + "friendlyName": "Order - Room Data", + "channel": "room_data", + "type": "String", + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"STRING" + "command": "set_properties", + "parameterType": "STRING" } ] }, { - "property":"time-zone1", - "siid":8, - "piid":14, - "friendlyName":"Order - Time Zone", - "channel":"time_zone1", - "type":"Number", - "stateDescription":{ - "step":1, - "pattern":"%.0f" + "property": "time-zone1", + "siid": 8, + "piid": 14, + "friendlyName": "Order - Time Zone", + "channel": "time_zone1", + "type": "Number", + "stateDescription": { + "step": 1, + "pattern": "%.0f" }, - "refresh":false, - "actions":[ + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"NUMBER" + "command": "set_properties", + "parameterType": "NUMBER" } ] }, { - "property":"all-enable-count", - "siid":8, - "piid":18, - "friendlyName":"Order - All Enable Count", - "channel":"all_enable_count", - "type":"String", - "stateDescription":{ - "readOnly":true + "property": "all-enable-count", + "siid": 8, + "piid": 18, + "friendlyName": "Order - All Enable Count", + "channel": "all_enable_count", + "type": "String", + "stateDescription": { + "readOnly": true }, - "refresh":true, - "actions":[ - - ] + "refresh": true, + "actions": [] }, { - "property":"zone-points", - "siid":9, - "piid":2, - "friendlyName":"Point Zone - Zone Points", - "channel":"zone_points", - "type":"String", - "refresh":false, - "actions":[ + "property": "zone-points", + "siid": 9, + "piid": 2, + "friendlyName": "Point Zone - Zone Points", + "channel": "zone_points", + "type": "String", + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"STRING" + "command": "set_properties", + "parameterType": "STRING" } ] }, { - "property":"restrict-points", - "siid":9, - "piid":3, - "friendlyName":"Point Zone - Restrict Points", - "channel":"restrict_points", - "type":"String", - "refresh":false, - "actions":[ + "property": "restrict-points", + "siid": 9, + "piid": 3, + "friendlyName": "Point Zone - Restrict Points", + "channel": "restrict_points", + "type": "String", + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"STRING" + "command": "set_properties", + "parameterType": "STRING" } ] }, { - "property":"target-point", - "siid":9, - "piid":5, - "friendlyName":"Point Zone - Target Point", - "channel":"target_point", - "type":"String", - "refresh":false, - "actions":[ + "property": "target-point", + "siid": 9, + "piid": 5, + "friendlyName": "Point Zone - Target Point", + "channel": "target_point", + "type": "String", + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"STRING" + "command": "set_properties", + "parameterType": "STRING" } ] }, { - "property":"remember-state", - "siid":10, - "piid":1, - "friendlyName":"Map - Remember State", - "channel":"remember_state", - "type":"Switch", - "refresh":false, - "actions":[ + "property": "remember-state", + "siid": 10, + "piid": 1, + "friendlyName": "Map - Remember State", + "channel": "remember_state", + "type": "Switch", + "refresh": false, + "actions": [ { - "command":"set_properties", - "parameterType":"ONOFFNUMBER" + "command": "set_properties", + "parameterType": "ONOFFNUMBER" } ] }, { - "property":"cur-map-id", - "siid":10, - "piid":2, - "friendlyName":"Map - Cur Map Id", - "channel":"cur_map_id", - "type":"Number", - "stateDescription":{ - "minimum":0, - "step":1, - "pattern":"%.0f", - "readOnly":true + "property": "cur-map-id", + "siid": 10, + "piid": 2, + "friendlyName": "Map - Cur Map Id", + "channel": "cur_map_id", + "type": "Number", + "stateDescription": { + "minimum": 0, + "step": 1, + "pattern": "%.0f", + "readOnly": true }, - "refresh":false, - "actions":[ - - ] - }, - { - "property":"map-num", - "siid":10, - "piid":3, - "friendlyName":"Map - Map Num", - "channel":"map_num", - "type":"Number", - "stateDescription":{ - "minimum":0, - "maximum":5, - "step":1, - "pattern":"%.0f", - "readOnly":true + "refresh": false, + "actions": [] + }, + { + "property": "map-num", + "siid": 10, + "piid": 3, + "friendlyName": "Map - Map Num", + "channel": "map_num", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 5, + "step": 1, + "pattern": "%.0f", + "readOnly": true }, - "refresh":false, - "actions":[ - - ] + "refresh": false, + "actions": [] }, { - "property":"cur-cleaning-path", - "siid":10, - "piid":5, - "friendlyName":"Map - Cur Cleaning Path", - "channel":"cur_cleaning_path", - "type":"String", - "stateDescription":{ - "readOnly":true + "property": "cur-cleaning-path", + "siid": 10, + "piid": 5, + "friendlyName": "Map - Cur Cleaning Path", + "channel": "cur_cleaning_path", + "type": "String", + "stateDescription": { + "readOnly": true }, - "refresh":false, - "actions":[ - - ] + "refresh": false, + "actions": [] }, { - "property":"build-map", - "siid":10, - "piid":14, - "friendlyName":"Map - Build Map", - "channel":"build_map", - "type":"Number", - "stateDescription":{ - "readOnly":true, - "options":[ + "property": "build-map", + "siid": 10, + "piid": 14, + "friendlyName": "Map - Build Map", + "channel": "build_map", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ { - "value":"0", - "label":"None" + "value": "0", + "label": "None" }, { - "value":"1", - "label":"Build" + "value": "1", + "label": "Build" }, { - "value":"2", - "label":"Clean" + "value": "2", + "label": "Clean" } ] }, - "refresh":false, - "actions":[ - - ], - "readmeComment":"Value mapping [\"0\"\u003d\"None\",\"1\"\u003d\"Build\",\"2\"\u003d\"Clean\"]" - }, - { - "property":"has-new-map", - "siid":10, - "piid":19, - "friendlyName":"Map - Has New Map", - "channel":"has_new_map", - "type":"Contact", - "stateDescription":{ - "readOnly":true + "refresh": false, + "actions": [], + "readmeComment": "Value mapping `[\"0\"\u003d\"None\",\"1\"\u003d\"Build\",\"2\"\u003d\"Clean\"]`" + }, + { + "property": "has-new-map", + "siid": 10, + "piid": 19, + "friendlyName": "Map - Has New Map", + "channel": "has_new_map", + "type": "Contact", + "stateDescription": { + "readOnly": true }, - "refresh":false, - "actions":[ - - ] + "refresh": false, + "actions": [] }, { - "property":"dnd-enable", - "siid":12, - "piid":1, - "friendlyName":"Disturb - Dnd Enable", - "channel":"dnd_enable", - "type":"Contact", - "stateDescription":{ - "readOnly":true + "property": "dnd-enable", + "siid": 12, + "piid": 1, + "friendlyName": "Disturb - Dnd Enable", + "channel": "dnd_enable", + "type": "Contact", + "stateDescription": { + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"dnd-start-hour", - "siid":12, - "piid":2, - "friendlyName":"Disturb - Dnd Start Hour", - "channel":"dnd_start_hour", - "type":"Number:Time", - "unit":"hours", - "stateDescription":{ - "minimum":0, - "maximum":23, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "dnd-start-hour", + "siid": 12, + "piid": 2, + "friendlyName": "Disturb - Dnd Start Hour", + "channel": "dnd_start_hour", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 23, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"dnd-start-minute", - "siid":12, - "piid":3, - "friendlyName":"Disturb - Dnd Start Minute", - "channel":"dnd_start_minute", - "type":"Number:Time", - "unit":"minutes", - "stateDescription":{ - "minimum":0, - "maximum":59, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "dnd-start-minute", + "siid": 12, + "piid": 3, + "friendlyName": "Disturb - Dnd Start Minute", + "channel": "dnd_start_minute", + "type": "Number:Time", + "unit": "minutes", + "stateDescription": { + "minimum": 0, + "maximum": 59, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"dnd-end-hour", - "siid":12, - "piid":4, - "friendlyName":"Disturb - Dnd End Hour", - "channel":"dnd_end_hour", - "type":"Number:Time", - "unit":"hours", - "stateDescription":{ - "minimum":0, - "maximum":23, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "dnd-end-hour", + "siid": 12, + "piid": 4, + "friendlyName": "Disturb - Dnd End Hour", + "channel": "dnd_end_hour", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 23, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"dnd-end-minute", - "siid":12, - "piid":5, - "friendlyName":"Disturb - Dnd End Minute", - "channel":"dnd_end_minute", - "type":"Number:Time", - "unit":"minutes", - "stateDescription":{ - "minimum":0, - "maximum":59, - "step":1, - "pattern":"%.0f %unit%", - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "dnd-end-minute", + "siid": 12, + "piid": 5, + "friendlyName": "Disturb - Dnd End Minute", + "channel": "dnd_end_minute", + "type": "Number:Time", + "unit": "minutes", + "stateDescription": { + "minimum": 0, + "maximum": 59, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true }, - "refresh":true, - "actions":[ - - ] - }, - { - "property":"multi-prop-dnd", - "siid":12, - "piid":7, - "friendlyName":"Disturb - Multi Prop Dnd", - "channel":"multi_prop_dnd", - "type":"String", - "stateDescription":{ - "readOnly":true + "refresh": true, + "actions": [] + }, + { + "property": "multi-prop-dnd", + "siid": 12, + "piid": 7, + "friendlyName": "Disturb - Multi Prop Dnd", + "channel": "multi_prop_dnd", + "type": "String", + "stateDescription": { + "readOnly": true }, - "refresh":true, - "actions":[ - - ] + "refresh": true, + "actions": [] } ], - "experimental":false + "experimental": false } } diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/mijia.vacuum.v2-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/mijia.vacuum.v2-miot.json index 12655bf5cf46a..8753a92746029 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/mijia.vacuum.v2-miot.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/mijia.vacuum.v2-miot.json @@ -657,35 +657,35 @@ }, { "value": "1", - "label": "简体中文" + "label": "Chinese" }, { "value": "2", - "label": "Español" + "label": "Spanish" }, { "value": "3", - "label": "Русский" + "label": "Russian" }, { "value": "4", - "label": "Italiano" + "label": "Italian" }, { "value": "5", - "label": "Français" + "label": "French" }, { "value": "6", - "label": "Deutsch" + "label": "German" }, { "value": "7", - "label": "한국어" + "label": "Korean" }, { "value": "8", - "label": "Polski" + "label": "Polish" } ] }, @@ -696,7 +696,7 @@ "parameterType": "NUMBER" } ], - "readmeComment": "Value mapping `[\"0\"\u003d\"English\",\"1\"\u003d\"简体中文\",\"2\"\u003d\"Español\",\"3\"\u003d\"Русский\",\"4\"\u003d\"Italiano\",\"5\"\u003d\"Français\",\"6\"\u003d\"Deutsch\",\"7\"\u003d\"한국어\",\"8\"\u003d\"Polski\"]`" + "readmeComment": "Value mapping `[\"0\"\u003d\"English\",\"1\"\u003d\"Chinese\",\"2\"\u003d\"Spanish\",\"3\"\u003d\"Russian\",\"4\"\u003d\"Italian\",\"5\"\u003d\"French\",\"6\"\u003d\"German\",\"7\"\u003d\"Korean\",\"8\"\u003d\"Polish\"]`" }, { "property": "not-disturb-switch", diff --git a/bundles/org.openhab.binding.miio/src/main/resources/misc/device_names.json b/bundles/org.openhab.binding.miio/src/main/resources/misc/device_names.json index 5316fe681c90d..c856d038cb031 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/misc/device_names.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/misc/device_names.json @@ -1550,7 +1550,6 @@ "ijai.vacuum.v19": "Xiaomi Robot Vacuum-Mop 2S", "ijai.vacuum.v2": "Mi Robot Vacuum-Mop 2", "ijai.vacuum.v3": "Mi Robot Vacuum-Mop 2 Pro", - "ijai.vacuum.v19": "Mi Robot Vacuum-Mop 2S", "ijomoo.airer.mja6": "JOMOO smart clothes dryer -A6090", "ijomoo.bhf_light.jd071": "JOMOO Smart Bath Heater JD071", "ijomoo.toilet.i90": "JOMOO Smart Toilet I90", diff --git a/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ReadmeHelper.java b/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ReadmeHelper.java index a16a707ea6f50..c5b374771b5f1 100644 --- a/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ReadmeHelper.java +++ b/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ReadmeHelper.java @@ -51,6 +51,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; import com.google.gson.stream.JsonReader; /** @@ -260,7 +261,7 @@ private void checkDatabaseEntrys() { try { JsonReader reader = new JsonReader(new FileReader(DEVICE_NAMES_FILE)); names = gson.fromJson(reader, names.getClass()); - } catch (IOException e) { + } catch (JsonSyntaxException | IOException e) { LOGGER.info("Error reading name list {}: ", DEVICE_NAMES_FILE, e.getMessage()); } From 4163775e0b036d723e7d29628dcd6540284cc469 Mon Sep 17 00:00:00 2001 From: openhab-bot Date: Tue, 29 Nov 2022 19:35:06 +0100 Subject: [PATCH 8/9] New translations mqtt.properties (Italian) (#13809) --- .../main/resources/OH-INF/i18n/mqtt_it.properties | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/i18n/mqtt_it.properties b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/i18n/mqtt_it.properties index 3d9197da2bec5..0894258e733fa 100644 --- a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/i18n/mqtt_it.properties +++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/i18n/mqtt_it.properties @@ -1,7 +1,7 @@ # thing types thing-type.mqtt.cct.label = Milight CCT -thing-type.mqtt.cct.description = Lampadina Led con comandi bianchi freddi e caldi +thing-type.mqtt.cct.description = Lampada LED con comandi per bianchi freddi e caldi thing-type.mqtt.fut089.label = Milight FUT089 thing-type.mqtt.fut089.description = Usa questo quando il tuo telecomando è il nuovo tipo con 8 gruppi chiamato FUT089 e le tue lampadine sono rgb_cct thing-type.mqtt.fut091.label = Milight FUT091 @@ -9,7 +9,7 @@ thing-type.mqtt.fut091.description = Usa questo quando il tuo telecomando è il thing-type.mqtt.rgb.label = Milight RGB thing-type.mqtt.rgb.description = Lampadina RGB senza bianco thing-type.mqtt.rgb_cct.label = Milight RGBCCT -thing-type.mqtt.rgb_cct.description = Lampadina Led a colori, e bianco sia freddo che caldo. +thing-type.mqtt.rgb_cct.description = Lampada LED a colori, e con bianchi freddi e caldi. thing-type.mqtt.rgbw.label = Milight RGBW thing-type.mqtt.rgbw.description = Lampadina RGB con un bianco fisso @@ -22,25 +22,25 @@ thing-type.config.mqtt.cct.oneTriggersNightMode.description = L'1% su un cursore thing-type.config.mqtt.rgb.oneTriggersNightMode.label = 1% Attiva Modalità Notturna thing-type.config.mqtt.rgb.oneTriggersNightMode.description = L'1% su un cursore attiverà la Modalità Notte. thing-type.config.mqtt.rgb.powerFailsToMinimum.label = Regolazione se fallisce Accensione -thing-type.config.mqtt.rgb.powerFailsToMinimum.description = Se luci perdono alimentazione quando in spegnimento software, the luci per default si accenderanno alla luminosità minima. +thing-type.config.mqtt.rgb.powerFailsToMinimum.description = Se le luci perdono luminosità quando vengono spente gradualmente, le luci torneranno alla luminosità minima per impostazione predefinita. thing-type.config.mqtt.rgbandcct.dimmedCT.label = Temp Colore Impostato thing-type.config.mqtt.rgbandcct.dimmedCT.description = Le tradizionali lampadine sono più calde più sono luminose. Imposta a 370, o lasciare vuoto per disabilitare. +thing-type.config.mqtt.rgbandcct.duvThreshold.label = Soglia Duv +thing-type.config.mqtt.rgbandcct.duvThreshold.description = Valori Duv uguali o inferiori a questo valore su un controllo di colore RGBWW attiveranno la modalità bianca alla temperatura di colore appropriata. 1 disabiliterà effettivamente questa funzione. Vedi questo link per ottenere maggiori informazioni su come viene calcolato. thing-type.config.mqtt.rgbandcct.favouriteWhite.label = Bianco Preferito thing-type.config.mqtt.rgbandcct.favouriteWhite.description = Quando uno shortcut attiva la modalità bianca, usala per il colore bianco. thing-type.config.mqtt.rgbandcct.oneTriggersNightMode.label = 1% Attiva modalità Notturna thing-type.config.mqtt.rgbandcct.oneTriggersNightMode.description = L'1% del cursore attiverà la Modalità Notte. thing-type.config.mqtt.rgbandcct.powerFailsToMinimum.label = Regolazione se fallisce Accensione -thing-type.config.mqtt.rgbandcct.powerFailsToMinimum.description = Se luci hanno perso l'alimentazione, si accenderanno alla luminosità minima. +thing-type.config.mqtt.rgbandcct.powerFailsToMinimum.description = Se manca l'alimentazione alle luci, si riaccenderanno alla luminosità minima. thing-type.config.mqtt.rgbandcct.whiteHue.label = Tonalità Bianca thing-type.config.mqtt.rgbandcct.whiteHue.description = Quando entrambi i valori WhiteHue e WhiteSat sono visti dal binding, si attiveranno il LED bianchi. thing-type.config.mqtt.rgbandcct.whiteSat.label = Saturazione Bianca thing-type.config.mqtt.rgbandcct.whiteSat.description = Quando entrambi i valori WhiteHue e WhiteSat sono visti dal binding, si attiveranno i LED bianchi. -thing-type.config.mqtt.rgbandcct.whiteThreshold.label = Soglia Bianco -thing-type.config.mqtt.rgbandcct.whiteThreshold.description = I valori di saturazione uguali o inferiori a questo valore su un controllo di colore RGBW attiveranno la modalità bianco. -1 disabiliterà questa funzione. thing-type.config.mqtt.rgbw.oneTriggersNightMode.label = 1% Attiva modalità Notturna thing-type.config.mqtt.rgbw.oneTriggersNightMode.description = L'1% sul cursore attiverà la Modalità Notte. thing-type.config.mqtt.rgbw.powerFailsToMinimum.label = Regolazione se fallisce Accensione -thing-type.config.mqtt.rgbw.powerFailsToMinimum.description = Se luci hanno perso l'alimentazione, si accenderanno alla luminosità minima. +thing-type.config.mqtt.rgbw.powerFailsToMinimum.description = Se manca l'alimentazione alle luci, si riaccenderanno alla luminosità minima. thing-type.config.mqtt.rgbw.whiteHue.label = Tonalità Bianca thing-type.config.mqtt.rgbw.whiteHue.description = Quando entrambi i valori WhiteHue e WhiteSat sono visti dal binding, si attiveranno il LED bianchi. thing-type.config.mqtt.rgbw.whiteSat.label = Saturazione Bianca From ae677dcd9d07bc265cf3b4d3083be9c0863f76be Mon Sep 17 00:00:00 2001 From: J-N-K Date: Tue, 29 Nov 2022 21:59:58 +0100 Subject: [PATCH 9/9] [jsscripting] Refactor dependency tracking (#13756) Signed-off-by: Jan N. Klug --- .../internal/GraalJSScriptEngineFactory.java | 19 ++++--- .../fs/watch/JSDependencyTracker.java | 29 +++++++++- .../internal/fs/watch/JSFileWatcher.java | 57 ------------------- .../fs/watch/JSScriptFileWatcher.java | 16 ++++-- 4 files changed, 50 insertions(+), 71 deletions(-) delete mode 100644 bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSFileWatcher.java diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java index 8a4745ea44668..31c69745eb290 100644 --- a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java +++ b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/GraalJSScriptEngineFactory.java @@ -12,13 +12,14 @@ */ package org.openhab.automation.jsscripting.internal; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import javax.script.ScriptEngine; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.automation.jsscripting.internal.fs.watch.JSDependencyTracker; +import org.openhab.core.automation.module.script.ScriptDependencyTracker; import org.openhab.core.automation.module.script.ScriptEngineFactory; import org.openhab.core.config.core.ConfigurableService; import org.osgi.framework.Constants; @@ -39,6 +40,7 @@ public final class GraalJSScriptEngineFactory implements ScriptEngineFactory { private static final String CFG_INJECTION_ENABLED = "injectionEnabled"; private static final String INJECTION_CODE = "Object.assign(this, require('openhab'));"; + private final JSDependencyTracker jsDependencyTracker; private boolean injectionEnabled = true; public static final String MIME_TYPE = "application/javascript;version=ECMAScript-2021"; @@ -46,14 +48,14 @@ public final class GraalJSScriptEngineFactory implements ScriptEngineFactory { @Activate public GraalJSScriptEngineFactory(final @Reference JSScriptServiceUtil jsScriptServiceUtil, - Map config) { + final @Reference JSDependencyTracker jsDependencyTracker, Map config) { + this.jsDependencyTracker = jsDependencyTracker; this.jsScriptServiceUtil = jsScriptServiceUtil; modified(config); } @Override public List getScriptTypes() { - List scriptTypes = new ArrayList<>(); /* * Whilst we run in parallel with Nashorn, we use a custom mime-type to avoid @@ -66,9 +68,7 @@ public List getScriptTypes() { // scriptTypes.addAll(graalJSEngineFactory.getMimeTypes()); // scriptTypes.addAll(graalJSEngineFactory.getExtensions()); - scriptTypes.add(MIME_TYPE); - - return Collections.unmodifiableList(scriptTypes); + return List.of(MIME_TYPE); } @Override @@ -82,6 +82,11 @@ public ScriptEngine createScriptEngine(String scriptType) { new OpenhabGraalJSScriptEngine(injectionEnabled ? INJECTION_CODE : null, jsScriptServiceUtil)); } + @Override + public @Nullable ScriptDependencyTracker getDependencyTracker() { + return jsDependencyTracker; + } + @Modified protected void modified(Map config) { Object injectionEnabled = config.get(CFG_INJECTION_ENABLED); diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSDependencyTracker.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSDependencyTracker.java index f4cb934d9dc28..c8cb1c669027a 100644 --- a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSDependencyTracker.java +++ b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSDependencyTracker.java @@ -14,8 +14,16 @@ import java.io.File; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.OpenHAB; -import org.openhab.core.automation.module.script.rulesupport.loader.DependencyTracker; +import org.openhab.core.automation.module.script.ScriptDependencyTracker; +import org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptDependencyTracker; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,7 +32,9 @@ * * @author Jonathan Gilbert - Initial contribution */ -public class JSDependencyTracker extends DependencyTracker { +@Component(service = JSDependencyTracker.class) +@NonNullByDefault +public class JSDependencyTracker extends AbstractScriptDependencyTracker { private final Logger logger = LoggerFactory.getLogger(JSDependencyTracker.class); @@ -35,6 +45,7 @@ public JSDependencyTracker() { super(LIB_PATH); } + @Activate public void activate() { File directory = new File(LIB_PATH); if (!directory.exists()) { @@ -47,4 +58,18 @@ public void activate() { super.activate(); } + + @Deactivate + public void deactivate() { + super.deactivate(); + } + + @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, unbind = "removeChangeTracker") + public void addChangeTracker(ScriptDependencyTracker.Listener listener) { + super.addChangeTracker(listener); + } + + public void removeChangeTracker(ScriptDependencyTracker.Listener listener) { + super.removeChangeTracker(listener); + } } diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSFileWatcher.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSFileWatcher.java deleted file mode 100644 index c801c3f49aea1..0000000000000 --- a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSFileWatcher.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.automation.jsscripting.internal.fs.watch; - -import org.openhab.core.automation.module.script.ScriptEngineManager; -import org.openhab.core.service.ReadyService; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.component.annotations.Reference; - -/** - * Monitors /automation/js for Javascript files & libraries. - * - * This class is required to ensure that the *order* of set up is correct. Specifically, the dependency tracker must - * be activated after the script file watcher. This is because AbstractWatchService only allows a single service to - * watch a single directory, and given that the watchers are nested and the last registration wins, the one we want to - * monitor the libraries must be registered last. - * - * @author Jonathan Gilbert - Initial contribution - */ -@Component(immediate = true, service = JSFileWatcher.class) -public class JSFileWatcher { - - private final JSScriptFileWatcher jsScriptFileWatcher; - private final JSDependencyTracker jsDependencyTracker; - - @Activate - public JSFileWatcher(final @Reference ScriptEngineManager manager, final @Reference ReadyService readyService) { - jsDependencyTracker = new JSDependencyTracker(); - jsScriptFileWatcher = new JSScriptFileWatcher(manager, readyService, jsDependencyTracker); - } - - @Activate - public void activate() { - jsScriptFileWatcher.activate(); - jsDependencyTracker.activate(); - jsDependencyTracker.addChangeTracker(jsScriptFileWatcher); - } - - @Deactivate - void deactivate() { - jsDependencyTracker.removeChangeTracker(jsScriptFileWatcher); - jsDependencyTracker.deactivate(); - jsScriptFileWatcher.deactivate(); - } -} diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSScriptFileWatcher.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSScriptFileWatcher.java index 2285c26903dfb..817e64a8ddb3d 100644 --- a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSScriptFileWatcher.java +++ b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/fs/watch/JSScriptFileWatcher.java @@ -20,24 +20,30 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.automation.jsscripting.internal.GraalJSScriptEngineFactory; +import org.openhab.core.automation.module.script.ScriptDependencyTracker; import org.openhab.core.automation.module.script.ScriptEngineManager; +import org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptFileWatcher; import org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileReference; -import org.openhab.core.automation.module.script.rulesupport.loader.ScriptFileWatcher; import org.openhab.core.service.ReadyService; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; /** * Monitors /automation/js for Javascript files, but not libraries * * @author Jonathan Gilbert - Initial contribution */ -public class JSScriptFileWatcher extends ScriptFileWatcher { +@Component(immediate = true, service = ScriptDependencyTracker.Listener.class) +public class JSScriptFileWatcher extends AbstractScriptFileWatcher { private static final String FILE_DIRECTORY = "automation" + File.separator + "js"; private final String ignorePath; - public JSScriptFileWatcher(final ScriptEngineManager manager, final ReadyService readyService, - JSDependencyTracker dependencyTracker) { - super(manager, dependencyTracker, readyService, FILE_DIRECTORY); + @Activate + public JSScriptFileWatcher(final @Reference ScriptEngineManager manager, + final @Reference ReadyService readyService) { + super(manager, readyService, FILE_DIRECTORY); ignorePath = pathToWatch + File.separator + "node_modules"; }