diff --git a/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/HomeConnectBindingConstants.java b/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/HomeConnectBindingConstants.java index 4d2dd432c08ca..f07647d256cc8 100644 --- a/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/HomeConnectBindingConstants.java +++ b/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/HomeConnectBindingConstants.java @@ -195,6 +195,26 @@ public class HomeConnectBindingConstants { public static final String OPTION_HOOD_VENTING_LEVEL = "Cooking.Common.Option.Hood.VentingLevel"; public static final String OPTION_HOOD_INTENSIVE_LEVEL = "Cooking.Common.Option.Hood.IntensiveLevel"; + // List of washer temperatures + public static final String TEMPERATURE_PREFIX = "LaundryCare.Washer.EnumType.Temperature."; + public static final String TEMPERATURE_AUTO = TEMPERATURE_PREFIX + "Auto"; + public static final String TEMPERATURE_COLD = TEMPERATURE_PREFIX + "Cold"; + public static final String TEMPERATURE_20 = TEMPERATURE_PREFIX + "GC20"; + public static final String TEMPERATURE_30 = TEMPERATURE_PREFIX + "GC30"; + public static final String TEMPERATURE_40 = TEMPERATURE_PREFIX + "GC40"; + public static final String TEMPERATURE_60 = TEMPERATURE_PREFIX + "GC60"; + public static final String TEMPERATURE_90 = TEMPERATURE_PREFIX + "GC90"; + + // List of spin speeds + public static final String SPIN_SPEED_PREFIX = "LaundryCare.Washer.EnumType.SpinSpeed."; + public static final String SPIN_SPEED_AUTO = SPIN_SPEED_PREFIX + "Auto"; + public static final String SPIN_SPEED_OFF = SPIN_SPEED_PREFIX + "Off"; + public static final String SPIN_SPEED_400 = SPIN_SPEED_PREFIX + "RPM400"; + public static final String SPIN_SPEED_600 = SPIN_SPEED_PREFIX + "RPM600"; + public static final String SPIN_SPEED_800 = SPIN_SPEED_PREFIX + "RPM800"; + public static final String SPIN_SPEED_1200 = SPIN_SPEED_PREFIX + "RPM1200"; + public static final String SPIN_SPEED_1400 = SPIN_SPEED_PREFIX + "RPM1400"; + // List of stages public static final String STAGE_FAN_OFF = "Cooking.Hood.EnumType.Stage.FanOff"; public static final String STAGE_FAN_STAGE_01 = "Cooking.Hood.EnumType.Stage.FanStage01"; diff --git a/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/client/HomeConnectApiClient.java b/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/client/HomeConnectApiClient.java index 0b1e4473d0c30..5c48d1f1c7d58 100644 --- a/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/client/HomeConnectApiClient.java +++ b/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/client/HomeConnectApiClient.java @@ -19,11 +19,9 @@ import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -79,7 +77,6 @@ public class HomeConnectApiClient { private final Logger logger = LoggerFactory.getLogger(HomeConnectApiClient.class); private final HttpClient client; private final String apiUrl; - private final Map> programsCache; private final OAuthClientService oAuthClientService; private final CircularQueue communicationQueue; private final ApiBridgeConfiguration apiBridgeConfiguration; @@ -90,7 +87,6 @@ public HomeConnectApiClient(HttpClient httpClient, OAuthClientService oAuthClien this.oAuthClientService = oAuthClientService; this.apiBridgeConfiguration = apiBridgeConfiguration; - programsCache = new ConcurrentHashMap<>(); apiUrl = simulated ? API_SIMULATOR_BASE_URL : API_BASE_URL; communicationQueue = new CircularQueue<>(COMMUNICATION_QUEUE_SIZE); if (apiRequestHistory != null) { @@ -610,16 +606,7 @@ public void stopProgram(String haId) public List getPrograms(String haId) throws CommunicationException, AuthorizationException, ApplianceOfflineException { - List programs; - if (programsCache.containsKey(haId)) { - logger.debug("Returning cached programs for '{}'.", haId); - programs = programsCache.get(haId); - programs = programs != null ? programs : Collections.emptyList(); - } else { - programs = getAvailablePrograms(haId, BASE_PATH + haId + "/programs"); - programsCache.put(haId, programs); - } - return programs; + return getAvailablePrograms(haId, BASE_PATH + haId + "/programs"); } public List getAvailablePrograms(String haId) diff --git a/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/client/model/AvailableProgram.java b/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/client/model/AvailableProgram.java index 54acbcc73819d..80ee852c6ab30 100644 --- a/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/client/model/AvailableProgram.java +++ b/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/client/model/AvailableProgram.java @@ -18,24 +18,39 @@ * AvailableProgram model * * @author Jonas Brüstel - Initial contribution + * @author Laurent Garnier - field "supported" added * */ @NonNullByDefault public class AvailableProgram { private final String key; + private final boolean supported; private final boolean available; private final String execution; - public AvailableProgram(String key, boolean available, String execution) { + public AvailableProgram(String key, boolean supported, boolean available, String execution) { this.key = key; + this.supported = supported; this.available = available; this.execution = execution; } + public AvailableProgram(String key, boolean available, String execution) { + this(key, true, available, execution); + } + + public AvailableProgram(String key, boolean supported) { + this(key, supported, true, ""); + } + public String getKey() { return key; } + public boolean isSupported() { + return supported; + } + public boolean isAvailable() { return available; } @@ -46,6 +61,7 @@ public String getExecution() { @Override public String toString() { - return "AvailableProgram [key=" + key + ", available=" + available + ", execution=" + execution + "]"; + return "AvailableProgram [key=" + key + ", supported=" + supported + ", available=" + available + ", execution=" + + execution + "]"; } } diff --git a/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/handler/AbstractHomeConnectThingHandler.java b/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/handler/AbstractHomeConnectThingHandler.java index 906d9d721e7c1..836715ba5b21a 100644 --- a/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/handler/AbstractHomeConnectThingHandler.java +++ b/bundles/org.openhab.binding.homeconnect/src/main/java/org/openhab/binding/homeconnect/internal/handler/AbstractHomeConnectThingHandler.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -44,6 +45,7 @@ import org.openhab.binding.homeconnect.internal.client.exception.AuthorizationException; import org.openhab.binding.homeconnect.internal.client.exception.CommunicationException; import org.openhab.binding.homeconnect.internal.client.listener.HomeConnectEventListener; +import org.openhab.binding.homeconnect.internal.client.model.AvailableProgram; import org.openhab.binding.homeconnect.internal.client.model.AvailableProgramOption; import org.openhab.binding.homeconnect.internal.client.model.Data; import org.openhab.binding.homeconnect.internal.client.model.Event; @@ -83,6 +85,7 @@ * sent to one of the channels. * * @author Jonas Brüstel - Initial contribution + * @author Laurent Garnier - programs cache moved and enhanced to allow adding unsupported programs */ @NonNullByDefault public abstract class AbstractHomeConnectThingHandler extends BaseThingHandler implements HomeConnectEventListener { @@ -105,7 +108,9 @@ public abstract class AbstractHomeConnectThingHandler extends BaseThingHandler i private final ExpiringStateMap expiringStateMap; private final AtomicBoolean accessible; private final Logger logger = LoggerFactory.getLogger(AbstractHomeConnectThingHandler.class); + private final List programsCache; private final Map> availableProgramOptionsCache; + private final Map> unsupportedProgramOptions; public AbstractHomeConnectThingHandler(Thing thing, HomeConnectDynamicStateDescriptionProvider dynamicStateDescriptionProvider) { @@ -115,10 +120,13 @@ public AbstractHomeConnectThingHandler(Thing thing, this.dynamicStateDescriptionProvider = dynamicStateDescriptionProvider; expiringStateMap = new ExpiringStateMap(Duration.ofSeconds(CACHE_TTL_SEC)); accessible = new AtomicBoolean(false); + programsCache = new CopyOnWriteArrayList<>(); availableProgramOptionsCache = new ConcurrentHashMap<>(); + unsupportedProgramOptions = new ConcurrentHashMap<>(); configureEventHandlers(eventHandlers); configureChannelUpdateHandlers(channelUpdateHandlers); + configureUnsupportedProgramOptions(unsupportedProgramOptions); } @Override @@ -207,7 +215,8 @@ && getBridgeHandler().isPresent()) { logger.debug("Start custom program. command={} haId={}", command.toFullString(), getThingHaId()); apiClient.startCustomProgram(getThingHaId(), command.toFullString()); } - } else if (command instanceof StringType && CHANNEL_SELECTED_PROGRAM_STATE.equals(channelUID.getId())) { + } else if (command instanceof StringType && CHANNEL_SELECTED_PROGRAM_STATE.equals(channelUID.getId()) + && isProgramSupported(command.toFullString())) { apiClient.setSelectedProgram(getThingHaId(), command.toFullString()); } } @@ -347,20 +356,15 @@ protected void updateSelectedProgramStateDescription() { return; } - Optional apiClient = getApiClient(); - if (apiClient.isPresent()) { - try { - List stateOptions = apiClient.get().getPrograms(getThingHaId()).stream() - .map(p -> new StateOption(p.getKey(), mapStringType(p.getKey()))).collect(Collectors.toList()); + try { + List stateOptions = getPrograms().stream() + .map(p -> new StateOption(p.getKey(), mapStringType(p.getKey()))).collect(Collectors.toList()); - getThingChannel(CHANNEL_SELECTED_PROGRAM_STATE).ifPresent( - channel -> dynamicStateDescriptionProvider.setStateOptions(channel.getUID(), stateOptions)); - } catch (CommunicationException | ApplianceOfflineException | AuthorizationException e) { - logger.debug("Could not fetch available programs. thing={}, haId={}, error={}", getThingLabel(), - getThingHaId(), e.getMessage()); - removeSelectedProgramStateDescription(); - } - } else { + getThingChannel(CHANNEL_SELECTED_PROGRAM_STATE).ifPresent( + channel -> dynamicStateDescriptionProvider.setStateOptions(channel.getUID(), stateOptions)); + } catch (CommunicationException | ApplianceOfflineException | AuthorizationException e) { + logger.debug("Could not fetch available programs. thing={}, haId={}, error={}", getThingLabel(), + getThingHaId(), e.getMessage()); removeSelectedProgramStateDescription(); } } @@ -485,6 +489,9 @@ protected Optional getThingChannel(String channelId) { */ protected abstract void configureEventHandlers(final Map handlers); + protected void configureUnsupportedProgramOptions(final Map> programOptions) { + } + protected boolean isChannelLinkedToProgramOptionNotFullySupportedByApi() { return false; } @@ -1433,24 +1440,24 @@ protected void processProgramOptions(List