From 8413dd4768ec7093825e3345da0162d90d30aea9 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Wed, 31 Jan 2018 18:14:30 +0100 Subject: [PATCH 01/22] created AutoUpdateManager Signed-off-by: Simon Kaufmann --- .../thing/internal/AutoUpdateManagerTest.java | 293 ++++++++++++++++++ .../core/thing/binding/AutoUpdatePolicy.java | 41 +++ .../thing/internal/AutoUpdateManager.java | 284 +++++++++++++++++ .../CommandResultPredictionListener.java | 47 +++ 4 files changed, 665 insertions(+) create mode 100644 bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java create mode 100644 bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/AutoUpdatePolicy.java create mode 100644 bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java create mode 100644 bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/CommandResultPredictionListener.java diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java new file mode 100644 index 00000000000..a028907c67b --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java @@ -0,0 +1,293 @@ +package org.eclipse.smarthome.core.thing.internal; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.smarthome.core.events.Event; +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.CommandResultPredictionListener; +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.events.ItemCommandEvent; +import org.eclipse.smarthome.core.items.events.ItemEventFactory; +import org.eclipse.smarthome.core.items.events.ItemStateEvent; +import org.eclipse.smarthome.core.library.items.StringItem; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingRegistry; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.AutoUpdatePolicy; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; +import org.eclipse.smarthome.core.thing.link.ItemChannelLink; +import org.eclipse.smarthome.core.thing.link.ItemChannelLinkRegistry; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; + +public class AutoUpdateManagerTest { + + private static final ThingUID THING_UID_ONLINE = new ThingUID("test::mock"); + private static final ThingUID THING_UID_HANDLER_MISSING = new ThingUID("test::handlerMissing"); + private static final ChannelUID CHANNEL_UID_ONLINE_1 = new ChannelUID(THING_UID_ONLINE, "channel1"); + private static final ChannelUID CHANNEL_UID_ONLINE_2 = new ChannelUID(THING_UID_ONLINE, "channel2"); + private static final ChannelUID CHANNEL_UID_ONLINE_GONE = new ChannelUID(THING_UID_ONLINE, "gone"); + private static final ChannelUID CHANNEL_UID_HANDLER_MISSING = new ChannelUID(THING_UID_HANDLER_MISSING, "channel1"); + private ItemCommandEvent event; + private GenericItem item; + private @Mock EventPublisher mockEventPublisher; + private @Mock ItemChannelLinkRegistry mockLinkRegistry; + private @Mock ThingRegistry mockThingRegistry; + private @Mock Thing mockThingOnline; + private @Mock Thing mockThingHandlerMissing; + private @Mock ThingHandler mockHandler; + private @Mock CommandResultPredictionListener mockPredictionListener; + + private final List links = new LinkedList<>(); + private AutoUpdateManager aum; + + @Before + public void setup() { + initMocks(this); + event = ItemEventFactory.createCommandEvent("test", new StringType("AFTER")); + item = new StringItem("test"); + item.setState(new StringType("BEFORE")); + + when(mockLinkRegistry.stream()).then(answer -> links.stream()); + when(mockThingRegistry.get(eq(THING_UID_ONLINE))).thenReturn(mockThingOnline); + when(mockThingRegistry.get(eq(THING_UID_HANDLER_MISSING))).thenReturn(mockThingHandlerMissing); + when(mockThingOnline.getHandler()).thenReturn(mockHandler); + when(mockThingOnline.getChannel(eq(CHANNEL_UID_ONLINE_1.getId()))) + .thenReturn(ChannelBuilder.create(CHANNEL_UID_ONLINE_1, "String").build()); + when(mockThingOnline.getChannel(eq(CHANNEL_UID_ONLINE_2.getId()))) + .thenReturn(ChannelBuilder.create(CHANNEL_UID_ONLINE_2, "String").build()); + + aum = new AutoUpdateManager(); + aum.setItemChannelLinkRegistry(mockLinkRegistry); + aum.setEventPublisher(mockEventPublisher); + aum.setThingRegistry(mockThingRegistry); + aum.addStateRevertListener(mockPredictionListener); + } + + private void assertEvent(String expectedContent, String extectedSource) { + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(Event.class); + verify(mockEventPublisher).post(eventCaptor.capture()); + Event event = eventCaptor.getValue(); + assertTrue(event instanceof ItemStateEvent); + assertEquals(expectedContent, ((ItemStateEvent) event).getItemState().toFullString()); + assertEquals(extectedSource, event.getSource()); + assertNothingHappened(); + } + + private void assertChangeStateTo() { + verify(mockPredictionListener).changeStateTo(isA(Item.class), eq(new StringType("AFTER"))); + assertNothingHappened(); + } + + private void assertKeepCurrentState() { + verify(mockPredictionListener).keepCurrentState(isA(Item.class)); + assertNothingHappened(); + } + + private void assertNothingHappened() { + verifyNoMoreInteractions(mockEventPublisher); + verifyNoMoreInteractions(mockPredictionListener); + } + + @Test + public void testAutoUpdate_noLink() { + aum.receiveCommand(event, item); + + assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); + } + + @Test + public void testAutoUpdate_noPolicy() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + + aum.receiveCommand(event, item); + + assertChangeStateTo(); + } + + @Test + public void testAutoUpdate_noPolicy_thingOFFLINE() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.OFFLINE); + + aum.receiveCommand(event, item); + + assertKeepCurrentState(); + } + + @Test + public void testAutoUpdate_noPolicy_noHandler() { + links.add(new ItemChannelLink("test", CHANNEL_UID_HANDLER_MISSING)); + + aum.receiveCommand(event, item); + + assertKeepCurrentState(); + } + + @Test + public void testAutoUpdate_noPolicy_noThing() { + links.add(new ItemChannelLink("test", new ChannelUID(new ThingUID("test::missing"), "gone"))); + + aum.receiveCommand(event, item); + + assertKeepCurrentState(); + } + + @Test + public void testAutoUpdate_noPolicy_noChannel() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); + + aum.receiveCommand(event, item); + + assertKeepCurrentState(); + } + + @Test + public void testAutoUpdate_policyVETO_thingONLINE() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.VETO); + + aum.receiveCommand(event, item); + + assertNothingHappened(); + } + + @Test + public void testAutoUpdate_policyRECOMMEND() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.RECOMMEND); + + aum.receiveCommand(event, item); + + assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); + } + + @Test + public void testAutoUpdate_policyVETObeatsDEFAULT() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_2)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.VETO); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_2, AutoUpdatePolicy.DEFAULT); + + aum.receiveCommand(event, item); + + assertNothingHappened(); + } + + @Test + public void testAutoUpdate_policyVETObeatsRECOMMEND() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_2)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.VETO); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_2, AutoUpdatePolicy.RECOMMEND); + + aum.receiveCommand(event, item); + + assertNothingHappened(); + } + + @Test + public void testAutoUpdate_policyDEFAULTbeatsRECOMMEND() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_2)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.DEFAULT); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_2, AutoUpdatePolicy.RECOMMEND); + + aum.receiveCommand(event, item); + + assertChangeStateTo(); + } + + @Test + public void testAutoUpdate_errorInvalidatesVETO() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.RECOMMEND); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.VETO); + + aum.receiveCommand(event, item); + + assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); + } + + @Test + public void testAutoUpdate_errorInvalidatesVETO2() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.DEFAULT); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.VETO); + + aum.receiveCommand(event, item); + + assertChangeStateTo(); + } + + @Test + public void testAutoUpdate_errorInvalidatesDEFAULT() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.RECOMMEND); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.DEFAULT); + + aum.receiveCommand(event, item); + + assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); + } + + @Test + public void testAutoUpdate_multipleErrors() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.DEFAULT); + + aum.receiveCommand(event, item); + + assertKeepCurrentState(); + } + + @Test + public void testAutoUpdate_disabled() { + aum.modified(Collections.singletonMap(AutoUpdateManager.PROPERTY_ENABLED, "false")); + + aum.receiveCommand(event, item); + + assertNothingHappened(); + } + + @Test + public void testAutoUpdate_sendOptimisticUpdates() { + links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); + when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); + aum.modified(Collections.singletonMap(AutoUpdateManager.PROPERTY_SEND_OPTIMISTIC_UPDATES, "true")); + + aum.receiveCommand(event, item); + + verify(mockPredictionListener).changeStateTo(isA(Item.class), eq(new StringType("AFTER"))); + assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE_OPTIMISTIC); + } + +} diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/AutoUpdatePolicy.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/AutoUpdatePolicy.java new file mode 100644 index 00000000000..ca82db9c625 --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/AutoUpdatePolicy.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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.eclipse.smarthome.core.thing.binding; + +/** + * A binding's recommendation to the framework whether a state update should be automatically sent to an item if a + * command was received. + * + * @author Simon Kaufmann - initial contribution and API + * + */ +public enum AutoUpdatePolicy { + /** + * No automatic state update should be sent by the framework. The handler will make sure it sends a state update and + * it can to it better than just converting the command to a state. + */ + VETO, + + /** + * The binding does not care and the framework may do what it deems to be right. The state update which the + * framework will send out normally will correspond the command state anyway. This is the default of no other policy + * is set. + */ + DEFAULT, + + /** + * An automatic state update should be sent by the framework because no updates will be sent by the binding. + * This usually is the case when devices don't expose their current state to the handler. + */ + RECOMMEND, +} diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java new file mode 100644 index 00000000000..2583bfca78f --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java @@ -0,0 +1,284 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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.eclipse.smarthome.core.thing.internal; + +import static java.util.stream.Collectors.toSet; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.CommandResultPredictionListener; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.events.ItemCommandEvent; +import org.eclipse.smarthome.core.items.events.ItemEventFactory; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingRegistry; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.binding.AutoUpdatePolicy; +import org.eclipse.smarthome.core.thing.link.ItemChannelLinkRegistry; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Modified; +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; + +/** + * + * @author Simon Kaufmann - initial contribution and API + * + */ +@NonNullByDefault +@Component(immediate = true, service = { + AutoUpdateManager.class }, configurationPid = "org.eclipse.smarthome.autoupdatemanager", configurationPolicy = ConfigurationPolicy.OPTIONAL) +public class AutoUpdateManager { + + protected static final String EVENT_SOURCE = "org.eclipse.smarthome.core.autoupdate"; + protected static final String EVENT_SOURCE_OPTIMISTIC = "org.eclipse.smarthome.core.autoupdate.optimistic"; + + protected static final String PROPERTY_ENABLED = "enabled"; + protected static final String PROPERTY_SEND_OPTIMISTIC_UPDATES = "sendOptimisticUpdates"; + + private final Logger logger = LoggerFactory.getLogger(AutoUpdateManager.class); + + private @NonNullByDefault({}) ItemChannelLinkRegistry itemChannelLinkRegistry; + private @NonNullByDefault({}) ThingRegistry thingRegistry; + private @NonNullByDefault({}) EventPublisher eventPublisher; + private final Set commandResultPredictionListeners = new HashSet<>(); + + private final Map autoUpdatePolicies = new ConcurrentHashMap<>(); + + private boolean enabled = true; + private boolean sendOptimisticUpdates = false; + + private static enum Recommendation { + /* + * An automatic state update must be sent because no channels are linked to the item. + */ + REQUIRED, + + /* + * An automatic state update should be sent because none of the linked channels are capable to retrieve the + * actual state for their device. + */ + RECOMMENDED, + + /* + * An automatic state update may be sent in the optimistic anticipation of what the handler is going to send + * soon anyway. + */ + OPTIMISTIC, + + /* + * No automatic state update should be sent because at least one channel claims it can handle it better. + */ + DONT, + + /* + * There are channels linked to the item which in theory would do the state update, but none of them is + * currently able to communicate with their devices, hence no automatic state update should be done and the + * previous item state should be sent instead in order to revert any control. + */ + REVERT + } + + @Activate + protected void activate(Map configuration) { + modified(configuration); + } + + @Modified + protected void modified(Map configuration) { + Object valueEnabled = configuration.get(PROPERTY_ENABLED); + if (valueEnabled != null) { + enabled = Boolean.parseBoolean(valueEnabled.toString()); + } + + Object valueSendOptimisticUpdates = configuration.get(PROPERTY_SEND_OPTIMISTIC_UPDATES); + if (valueSendOptimisticUpdates != null) { + sendOptimisticUpdates = Boolean.parseBoolean(valueSendOptimisticUpdates.toString()); + } + } + + public void receiveCommand(ItemCommandEvent commandEvent, Item item) { + if (!enabled) { + return; + } + final String itemName = commandEvent.getItemName(); + final Command command = commandEvent.getItemCommand(); + if (command instanceof State) { + final State state = (State) command; + + // TODO: consider user-override via item meta-data + // (see https://github.com/eclipse/smarthome/pull/4390) + Recommendation autoUpdate = shouldAutoUpdate(itemName); + + switch (autoUpdate) { + case REQUIRED: + logger.trace("Automatically updating item '{}' because no channel is linked", itemName); + postUpdate(item, state, EVENT_SOURCE); + break; + case RECOMMENDED: + logger.trace("Automatically updating item '{}' because no channel does it", itemName); + postUpdate(item, state, EVENT_SOURCE); + break; + case OPTIMISTIC: + logger.trace("Optimistically updating item '{}'", itemName); + for (CommandResultPredictionListener listener : commandResultPredictionListeners) { + listener.changeStateTo(item, state); + } + if (sendOptimisticUpdates) { + postUpdate(item, state, EVENT_SOURCE_OPTIMISTIC); + } + break; + case DONT: + logger.trace("Won't update item '{}' as it was vetoed.", itemName); + break; + case REVERT: + logger.trace("Sending current item state to revert controls '{}'", itemName); + for (CommandResultPredictionListener listener : commandResultPredictionListeners) { + listener.keepCurrentState(item); + } + break; + } + } + } + + private Recommendation shouldAutoUpdate(String itemName) { + Recommendation ret = Recommendation.REQUIRED; + + Set linkedChannelUIDs = itemChannelLinkRegistry.stream().filter(link -> { + return link.getItemName().equals(itemName); + }).map(link -> { + return link.getLinkedUID(); + }).collect(toSet()); + for (ChannelUID channelUID : linkedChannelUIDs) { + Thing thing = thingRegistry.get(channelUID.getThingUID()); + if (thing == null // + || thing.getChannel(channelUID.getId()) == null // + || thing.getHandler() == null // + || !ThingStatus.ONLINE.equals(thing.getStatus()) // + ) { + if (ret == Recommendation.REQUIRED) { + ret = Recommendation.REVERT; + } + continue; + } + + AutoUpdatePolicy policy = autoUpdatePolicies.get(channelUID); + if (policy == null) { + policy = AutoUpdatePolicy.DEFAULT; + } + switch (policy) { + case VETO: + ret = Recommendation.DONT; + break; + case DEFAULT: + if (ret == Recommendation.REQUIRED || ret == Recommendation.RECOMMENDED) { + ret = Recommendation.OPTIMISTIC; + } + break; + case RECOMMEND: + if (ret == Recommendation.REQUIRED) { + ret = Recommendation.RECOMMENDED; + } + break; + } + } + return ret; + } + + private void postUpdate(Item item, State newState, String origin) { + boolean isAccepted = isAcceptedState(newState, item); + if (isAccepted) { + eventPublisher.post(ItemEventFactory.createStateEvent(item.getName(), newState, origin)); + } else { + logger.debug("Received update of a not accepted type ({}) for item {}", newState.getClass().getSimpleName(), + item.getName()); + } + } + + private boolean isAcceptedState(State newState, Item item) { + boolean isAccepted = false; + if (item.getAcceptedDataTypes().contains(newState.getClass())) { + isAccepted = true; + } else { + // Look for class hierarchy + for (Class state : item.getAcceptedDataTypes()) { + try { + if (!state.isEnum() && state.newInstance().getClass().isAssignableFrom(newState.getClass())) { + isAccepted = true; + break; + } + } catch (InstantiationException e) { + logger.warn("InstantiationException on {}", e.getMessage(), e); // Should never happen + } catch (IllegalAccessException e) { + logger.warn("IllegalAccessException on {}", e.getMessage(), e); // Should never happen + } + } + } + return isAccepted; + } + + protected void setAutoUpdatePolicy(ChannelUID channelUID, AutoUpdatePolicy policy) { + autoUpdatePolicies.put(channelUID, policy); + } + + @Reference + protected void setItemChannelLinkRegistry(ItemChannelLinkRegistry itemChannelLinkRegistry) { + this.itemChannelLinkRegistry = itemChannelLinkRegistry; + } + + protected void unsetItemChannelLinkRegistry(ItemChannelLinkRegistry itemChannelLinkRegistry) { + this.itemChannelLinkRegistry = null; + } + + @Reference + protected void setThingRegistry(ThingRegistry thingRegistry) { + this.thingRegistry = thingRegistry; + } + + protected void unsetThingRegistry(ThingRegistry thingRegistry) { + this.thingRegistry = null; + } + + @Reference + protected void setEventPublisher(EventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } + + protected void unsetEventPublisher(EventPublisher eventPublisher) { + this.eventPublisher = null; + } + + @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) + protected void addStateRevertListener(CommandResultPredictionListener commandResultPredictionListener) { + commandResultPredictionListeners.add(commandResultPredictionListener); + } + + protected void removeStateRevertListener(CommandResultPredictionListener commandResultPredictionListener) { + commandResultPredictionListeners.remove(commandResultPredictionListener); + } + +} diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/CommandResultPredictionListener.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/CommandResultPredictionListener.java new file mode 100644 index 00000000000..1fe03386faf --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/CommandResultPredictionListener.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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.eclipse.smarthome.core.items; + +import org.eclipse.smarthome.core.types.State; + +/** + * Interface which announces potential item state outcomes when a command was received. + *

+ * This interface is intended to be implemented only within the framework itself and not meant to be implemented by + * bindings. + * + * @author Simon Kaufmann - initial contribution and API + * + */ +public interface CommandResultPredictionListener { + + /** + * Denotes that the item state is most likely going to change to the given value. + * + * @param item + * @param state + */ + void changeStateTo(Item item, State state); + + /** + * Reconfirms the previous item state because a command for sure will not be successfully executed. + *

+ * This is actually the exact opposite of {@link #changeStateTo(Item, State)}: it denotes that a recently received + * command presumably will not result in a state change, e.g. because no handler currently is capable of delivering + * such an event to its device. + * + * @param item + */ + void keepCurrentState(Item item); + +} From 7932fb927e91cd875a475ab5e33ae54a461ef9de Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Wed, 31 Jan 2018 18:17:25 +0100 Subject: [PATCH 02/22] integrate AutoUpdateManager into ThingHandlerCallback Signed-off-by: Simon Kaufmann --- .../thing/binding/ThingHandlerCallback.java | 9 +++++++++ .../core/thing/internal/ThingManager.java | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java index f2e26b77c45..51e81877cd2 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java @@ -134,4 +134,13 @@ public interface ThingHandlerCallback { * @return true if at least one item is linked, false otherwise */ boolean isChannelLinked(ChannelUID channelUID); + + /** + * Give advice to the framework whether automatic state updates should be sent for the given channel or not. + * + * @param channelUID + * @param policy + */ + void setAutoUpdatePolicy(ChannelUID channelUID, AutoUpdatePolicy policy); + } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java index d322ec2c5c3..3514d0e684d 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java @@ -34,6 +34,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.ConfigDescription; import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; import org.eclipse.smarthome.config.core.ConfigDescriptionRegistry; @@ -60,6 +61,7 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.UID; +import org.eclipse.smarthome.core.thing.binding.AutoUpdatePolicy; import org.eclipse.smarthome.core.thing.binding.BridgeHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerCallback; @@ -127,6 +129,7 @@ public class ThingManager implements ThingTracker, ThingTypeMigrationService, Re private EventPublisher eventPublisher; private CommunicationManager communicationManager; + private AutoUpdateManager autoUpdateManager; private ReadyService readyService; @@ -299,6 +302,12 @@ public ChannelBuilder editChannel(Thing thing, ChannelUID channelUID) { public boolean isChannelLinked(ChannelUID channelUID) { return !itemChannelLinkRegistry.getLinks(channelUID).isEmpty(); } + + @Override + public void setAutoUpdatePolicy(@NonNull ChannelUID channelUID, @NonNull AutoUpdatePolicy policy) { + autoUpdateManager.setAutoUpdatePolicy(channelUID, policy); + } + }; private ThingRegistryImpl thingRegistry; @@ -1201,4 +1210,13 @@ protected void unsetSafeCaller(SafeCaller safeCaller) { this.safeCaller = null; } + @Reference + public void setAutoUpdateManager(AutoUpdateManager autoUpdateManager) { + this.autoUpdateManager = autoUpdateManager; + } + + public void unsetAutoUpdateManager(AutoUpdateManager autoUpdateManager) { + this.autoUpdateManager = null; + } + } From e650f75dbd2b600f2930253dd8dc7d5b05ec2115 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Wed, 31 Jan 2018 18:19:09 +0100 Subject: [PATCH 03/22] integrate AutoUpdateManager into CommunicationManager Signed-off-by: Simon Kaufmann --- .../internal/CommunicationManagerTest.java | 10 +++++++++- .../thing/internal/CommunicationManager.java | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/CommunicationManagerTest.java b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/CommunicationManagerTest.java index ee9b2175388..80caf6ef2b9 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/CommunicationManagerTest.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/CommunicationManagerTest.java @@ -26,7 +26,9 @@ import org.eclipse.smarthome.core.common.SafeCaller; import org.eclipse.smarthome.core.events.EventPublisher; import org.eclipse.smarthome.core.i18n.UnitProvider; +import org.eclipse.smarthome.core.items.Item; import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.items.events.ItemCommandEvent; import org.eclipse.smarthome.core.items.events.ItemEventFactory; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.items.NumberItem; @@ -131,6 +133,9 @@ public class CommunicationManagerTest extends JavaOSGiTest { @Mock private ThingHandler mockHandler; + @Mock + private AutoUpdateManager mockAutoUpdateManager; + private SafeCaller safeCaller; @Before @@ -189,8 +194,8 @@ public Stream stream() { when(thingRegistry.get(eq(THING_UID))).thenReturn(THING); manager.setThingRegistry(thingRegistry); - manager.addItemFactory(new CoreItemFactory()); + manager.setAutoUpdateManager(mockAutoUpdateManager); UnitProvider unitProvider = mock(UnitProvider.class); when(unitProvider.getUnit(Temperature.class)).thenReturn(SIUnits.CELSIUS); @@ -245,6 +250,7 @@ public void testItemCommandEvent_singleLink() { }); verifyNoMoreInteractions(stateProfile); verifyNoMoreInteractions(triggerProfile); + verify(mockAutoUpdateManager).receiveCommand(isA(ItemCommandEvent.class), isA(Item.class)); } @Test @@ -284,6 +290,7 @@ public void testItemCommandEvent_multiLink() { }); verifyNoMoreInteractions(stateProfile); verifyNoMoreInteractions(triggerProfile); + verify(mockAutoUpdateManager).receiveCommand(isA(ItemCommandEvent.class), isA(Item.class)); } @Test @@ -295,6 +302,7 @@ public void testItemCommandEvent_notToSource() { }); verifyNoMoreInteractions(stateProfile); verifyNoMoreInteractions(triggerProfile); + verify(mockAutoUpdateManager).receiveCommand(isA(ItemCommandEvent.class), isA(Item.class)); } @Test diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/CommunicationManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/CommunicationManager.java index 1de4fd3323b..a49b9a9086d 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/CommunicationManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/CommunicationManager.java @@ -110,6 +110,8 @@ public class CommunicationManager implements EventSubscriber, RegistryChangeList @NonNullByDefault({}) private SafeCaller safeCaller; @NonNullByDefault({}) + private AutoUpdateManager autoUpdateManager; + @NonNullByDefault({}) private ItemStateConverter itemStateConverter; private final Set itemFactories = new CopyOnWriteArraySet<>(); @@ -258,6 +260,12 @@ private boolean supportsProfileTypeUID(ProfileFactory profileFactory, ProfileTyp private void receiveCommand(ItemCommandEvent commandEvent) { final String itemName = commandEvent.getItemName(); final Command command = commandEvent.getItemCommand(); + final Item item = getItem(itemName); + + if (item != null) { + autoUpdateManager.receiveCommand(commandEvent, item); + } + handleEvent(itemName, command, commandEvent.getSource(), s -> acceptedCommandTypeMap.get(s), (profile, thing, convertedCommand) -> { if (profile instanceof StateProfile) { @@ -605,6 +613,15 @@ private synchronized void calculateAcceptedTypes() { } } + @Reference + public void setAutoUpdateManager(AutoUpdateManager autoUpdateManager) { + this.autoUpdateManager = autoUpdateManager; + } + + public void unsetAutoUpdateManager(AutoUpdateManager autoUpdateManager) { + this.autoUpdateManager = null; + } + private static class NoOpProfile implements Profile { @Override public @NonNull ProfileTypeUID getProfileTypeUID() { From 00884f1189ea3df71f1f839e3389cc31ffde4243 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Wed, 31 Jan 2018 18:47:10 +0100 Subject: [PATCH 04/22] dropped o.e.sh.core.autoupdate bundle Signed-off-by: Simon Kaufmann --- .../.classpath | 8 - .../.project | 33 ---- .../META-INF/MANIFEST.MF | 24 --- .../NOTICE | 19 -- .../OSGI-INF/binding.xml | 25 --- .../build.properties | 7 - .../pom.xml | 18 -- .../AutoUpdateBindingConfigProvider.java | 43 ---- .../internal/AutoUpdateBinding.java | 184 ------------------ .../src/main/resources/readme.txt | 1 - 10 files changed, 362 deletions(-) delete mode 100644 bundles/core/org.eclipse.smarthome.core.autoupdate/.classpath delete mode 100644 bundles/core/org.eclipse.smarthome.core.autoupdate/.project delete mode 100644 bundles/core/org.eclipse.smarthome.core.autoupdate/META-INF/MANIFEST.MF delete mode 100644 bundles/core/org.eclipse.smarthome.core.autoupdate/NOTICE delete mode 100644 bundles/core/org.eclipse.smarthome.core.autoupdate/OSGI-INF/binding.xml delete mode 100644 bundles/core/org.eclipse.smarthome.core.autoupdate/build.properties delete mode 100644 bundles/core/org.eclipse.smarthome.core.autoupdate/pom.xml delete mode 100644 bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/java/org/eclipse/smarthome/core/autoupdate/AutoUpdateBindingConfigProvider.java delete mode 100644 bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/java/org/eclipse/smarthome/core/autoupdate/internal/AutoUpdateBinding.java delete mode 100644 bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/resources/readme.txt diff --git a/bundles/core/org.eclipse.smarthome.core.autoupdate/.classpath b/bundles/core/org.eclipse.smarthome.core.autoupdate/.classpath deleted file mode 100644 index 2434f39bf8c..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.autoupdate/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/bundles/core/org.eclipse.smarthome.core.autoupdate/.project b/bundles/core/org.eclipse.smarthome.core.autoupdate/.project deleted file mode 100644 index b9afcda7388..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.autoupdate/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.eclipse.smarthome.core.autoupdate - This is the Auto-Update binding of Eclipse SmartHome - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.pde.PluginNature - - diff --git a/bundles/core/org.eclipse.smarthome.core.autoupdate/META-INF/MANIFEST.MF b/bundles/core/org.eclipse.smarthome.core.autoupdate/META-INF/MANIFEST.MF deleted file mode 100644 index b4e16f79da2..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.autoupdate/META-INF/MANIFEST.MF +++ /dev/null @@ -1,24 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: . -Bundle-License: https://www.eclipse.org/legal/epl-2.0/ -Bundle-ManifestVersion: 2 -Bundle-Name: Eclipse SmartHome AutoUpdate Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.eclipse.smarthome.core.autoupdate -Bundle-Vendor: Eclipse.org/SmartHome -Bundle-Version: 0.10.0.qualifier -Export-Package: org.eclipse.smarthome.core.autoupdate -Ignore-Package: org.eclipse.smarthome.core.autoupdate.internal -Import-Package: - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.core.autoupdate, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.items.events, - org.eclipse.smarthome.core.types, - org.osgi.framework, - org.slf4j -Private-Package: org.eclipse.smarthome.core.autoupdate.internal -Service-Component: OSGI-INF/*.xml -Automatic-Module-Name: org.eclipse.smarthome.core.autoupdate diff --git a/bundles/core/org.eclipse.smarthome.core.autoupdate/NOTICE b/bundles/core/org.eclipse.smarthome.core.autoupdate/NOTICE deleted file mode 100644 index b8675cd02e8..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.autoupdate/NOTICE +++ /dev/null @@ -1,19 +0,0 @@ -This content is produced and maintained by the Eclipse SmartHome project. - -* Project home: https://eclipse.org/smarthome/ - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/eclipse/smarthome - -== Copyright Holders - -See the NOTICE file distributed with the source code at -https://github.com/eclipse/smarthome/blob/master/NOTICE -for detailed information regarding copyright ownership. diff --git a/bundles/core/org.eclipse.smarthome.core.autoupdate/OSGI-INF/binding.xml b/bundles/core/org.eclipse.smarthome.core.autoupdate/OSGI-INF/binding.xml deleted file mode 100644 index 21d0f6b1a13..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.autoupdate/OSGI-INF/binding.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - diff --git a/bundles/core/org.eclipse.smarthome.core.autoupdate/build.properties b/bundles/core/org.eclipse.smarthome.core.autoupdate/build.properties deleted file mode 100644 index 88da2fca29b..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.autoupdate/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -output.. = target/classes/ -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - NOTICE -source.. = src/main/java/,\ - src/main/resources/ diff --git a/bundles/core/org.eclipse.smarthome.core.autoupdate/pom.xml b/bundles/core/org.eclipse.smarthome.core.autoupdate/pom.xml deleted file mode 100644 index ac321e6f209..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.autoupdate/pom.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - 4.0.0 - - - org.eclipse.smarthome.bundles - core - 0.10.0-SNAPSHOT - - org.eclipse.smarthome.core - org.eclipse.smarthome.core.autoupdate - - eclipse-plugin - - Eclipse SmartHome AutoUpdate Binding - - diff --git a/bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/java/org/eclipse/smarthome/core/autoupdate/AutoUpdateBindingConfigProvider.java b/bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/java/org/eclipse/smarthome/core/autoupdate/AutoUpdateBindingConfigProvider.java deleted file mode 100644 index ac28e728227..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/java/org/eclipse/smarthome/core/autoupdate/AutoUpdateBindingConfigProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * 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.eclipse.smarthome.core.autoupdate; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * This interface is implemented by classes that can provide configuration - * information of the AutoUpdate feature. - * - * Implementing classes should register themselves as a service in order to be - * taken into account. - * - * @author Thomas.Eichstaedt-Engelen - Initial contribution - * @author Kai Kreuzer - refactored to make independent of parent interfaces - */ -@NonNullByDefault -public interface AutoUpdateBindingConfigProvider { - - /** - * Indicates whether an Item with the given itemName is - * configured to automatically update it's State after receiving a Command - * or not. - * - * @param itemName the name of the Item for which to find the configuration - * @return false to disable the automatic update, true to enable the automatic update and - * null if there is no configuration for this item. - */ - @Nullable - Boolean autoUpdate(String itemName); - -} diff --git a/bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/java/org/eclipse/smarthome/core/autoupdate/internal/AutoUpdateBinding.java b/bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/java/org/eclipse/smarthome/core/autoupdate/internal/AutoUpdateBinding.java deleted file mode 100644 index 067a2aef3b1..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/java/org/eclipse/smarthome/core/autoupdate/internal/AutoUpdateBinding.java +++ /dev/null @@ -1,184 +0,0 @@ -/** - * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * 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.eclipse.smarthome.core.autoupdate.internal; - -import java.util.Collection; -import java.util.concurrent.CopyOnWriteArraySet; - -import org.eclipse.smarthome.core.autoupdate.AutoUpdateBindingConfigProvider; -import org.eclipse.smarthome.core.events.EventPublisher; -import org.eclipse.smarthome.core.items.GenericItem; -import org.eclipse.smarthome.core.items.ItemNotFoundException; -import org.eclipse.smarthome.core.items.ItemRegistry; -import org.eclipse.smarthome.core.items.events.AbstractItemEventSubscriber; -import org.eclipse.smarthome.core.items.events.ItemCommandEvent; -import org.eclipse.smarthome.core.items.events.ItemEventFactory; -import org.eclipse.smarthome.core.types.Command; -import org.eclipse.smarthome.core.types.State; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - *

- * The AutoUpdate-Binding is no 'normal' binding as it doesn't connect any hardware to the Eclipse SmartHome system. In - * fact it takes care of updating the State of an item with respect to the received command automatically or not. By - * default the State is getting updated automatically which is desired behavior in most of the cases. However it could - * be useful to disable this default behavior. - * - *

- * For example when implementing validation steps before changing a State one needs to control the State update oneself. - * - * @author Thomas.Eichstaedt-Engelen - Initial contribution - * @author Kai Kreuzer - added sending real events - * @author Stefan Bußweiler - Migration to new ESH event concept - */ -public class AutoUpdateBinding extends AbstractItemEventSubscriber { - - private final Logger logger = LoggerFactory.getLogger(AutoUpdateBinding.class); - - protected volatile ItemRegistry itemRegistry; - - /** to keep track of all binding config providers */ - protected Collection providers = new CopyOnWriteArraySet<>(); - - protected EventPublisher eventPublisher = null; - - public void addBindingConfigProvider(AutoUpdateBindingConfigProvider provider) { - providers.add(provider); - } - - public void removeBindingConfigProvider(AutoUpdateBindingConfigProvider provider) { - providers.remove(provider); - } - - public void setEventPublisher(EventPublisher eventPublisher) { - this.eventPublisher = eventPublisher; - } - - public void unsetEventPublisher(EventPublisher eventPublisher) { - this.eventPublisher = null; - } - - public void setItemRegistry(ItemRegistry itemRegistry) { - this.itemRegistry = itemRegistry; - } - - public void unsetItemRegistry(ItemRegistry itemRegistry) { - this.itemRegistry = null; - } - - /** - * Handle the received command event. - * - *

- * If the command could be converted to a {@link State} the auto-update configurations are inspected. - * If there is at least one configuration that enable the auto-update, auto-update is applied. - * If there is no configuration provided at all the autoupdate defaults to {@code true} and an update is posted for - * the corresponding {@link State}. - * - * @param commandEvent the command event - */ - @Override - protected void receiveCommand(ItemCommandEvent commandEvent) { - final Command command = commandEvent.getItemCommand(); - if (command instanceof State) { - final State state = (State) command; - - final String itemName = commandEvent.getItemName(); - - Boolean autoUpdate = autoUpdate(itemName); - - // we didn't find any autoupdate configuration, so apply the default now - if (autoUpdate == null) { - autoUpdate = Boolean.TRUE; - } - - if (autoUpdate) { - postUpdate(itemName, state); - } else { - logger.trace("Won't update item '{}' as it is not configured to update its state automatically.", - itemName); - } - } - } - - /** - * Check auto update configuration(s) for given item name. - * - * @param itemName the name of the item - * @return true if auto-update is enabled, false if auto-update is disabled, null if there is no explicit - * configuration - */ - private Boolean autoUpdate(final String itemName) { - Boolean autoUpdate = null; - - // Check auto update by configuration for item name - for (AutoUpdateBindingConfigProvider provider : providers) { - Boolean au = provider.autoUpdate(itemName); - if (au == null) { - // There is no setting for the item, keep value unchanged. - continue; - } else { - // There is an entry for the item. - if (au) { - // setting is true, we could stop. - return true; - } else { - // setting is false, set it if autoupdate is not set - if (autoUpdate == null) { - autoUpdate = false; - } - } - } - } - - return autoUpdate; - } - - private void postUpdate(String itemName, State newState) { - if (itemRegistry != null) { - try { - GenericItem item = (GenericItem) itemRegistry.getItem(itemName); - boolean isAccepted = false; - if (item.getAcceptedDataTypes().contains(newState.getClass())) { - isAccepted = true; - } else { - // Look for class hierarchy - for (Class state : item.getAcceptedDataTypes()) { - try { - if (!state.isEnum() - && state.newInstance().getClass().isAssignableFrom(newState.getClass())) { - isAccepted = true; - break; - } - } catch (InstantiationException e) { - logger.warn("InstantiationException on {}", e.getMessage(), e); // Should never happen - } catch (IllegalAccessException e) { - logger.warn("IllegalAccessException on {}", e.getMessage(), e); // Should never happen - } - } - } - if (isAccepted) { - eventPublisher.post(ItemEventFactory.createStateEvent(itemName, newState, - "org.eclipse.smarthome.core.autoupdate")); - } else { - logger.debug("Received update of a not accepted type ({}) for item {}", - newState.getClass().getSimpleName(), itemName); - } - } catch (ItemNotFoundException e) { - logger.debug("Received update for non-existing item: {}", e.getMessage()); - } - } - } - -} diff --git a/bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/resources/readme.txt b/bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/resources/readme.txt deleted file mode 100644 index 98698c670dc..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.autoupdate/src/main/resources/readme.txt +++ /dev/null @@ -1 +0,0 @@ -Bundle resources go in here! \ No newline at end of file From a7bb39efe2d597a69717d3a9376dccc096488e8b Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Wed, 31 Jan 2018 18:47:47 +0100 Subject: [PATCH 05/22] dropped random autoupdate package imports Signed-off-by: Simon Kaufmann --- .../META-INF/MANIFEST.MF | 1 - .../META-INF/MANIFEST.MF | 1 - .../META-INF/MANIFEST.MF | 1 - .../META-INF/MANIFEST.MF | 1 - 4 files changed, 4 deletions(-) diff --git a/bundles/automation/org.eclipse.smarthome.automation.event.test/META-INF/MANIFEST.MF b/bundles/automation/org.eclipse.smarthome.automation.event.test/META-INF/MANIFEST.MF index c2d2cddccb5..fd742582a12 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.event.test/META-INF/MANIFEST.MF +++ b/bundles/automation/org.eclipse.smarthome.automation.event.test/META-INF/MANIFEST.MF @@ -15,7 +15,6 @@ Import-Package: org.eclipse.smarthome.automation.template, org.eclipse.smarthome.automation.type, org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.autoupdate, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.events, org.eclipse.smarthome.core.items, diff --git a/bundles/automation/org.eclipse.smarthome.automation.integration.test/META-INF/MANIFEST.MF b/bundles/automation/org.eclipse.smarthome.automation.integration.test/META-INF/MANIFEST.MF index 821db8e6aa0..6badff97457 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.integration.test/META-INF/MANIFEST.MF +++ b/bundles/automation/org.eclipse.smarthome.automation.integration.test/META-INF/MANIFEST.MF @@ -21,7 +21,6 @@ Import-Package: org.eclipse.smarthome.automation.template, org.eclipse.smarthome.automation.type, org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.autoupdate, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.events, org.eclipse.smarthome.core.items, diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.test/META-INF/MANIFEST.MF b/bundles/automation/org.eclipse.smarthome.automation.module.script.test/META-INF/MANIFEST.MF index 96c740a818f..e24e23c018c 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.test/META-INF/MANIFEST.MF +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.test/META-INF/MANIFEST.MF @@ -17,7 +17,6 @@ Import-Package: org.eclipse.smarthome.automation.template, org.eclipse.smarthome.automation.type, org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.autoupdate, org.eclipse.smarthome.core.events, org.eclipse.smarthome.core.items, org.eclipse.smarthome.core.items.events, diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.timer.test/META-INF/MANIFEST.MF b/bundles/automation/org.eclipse.smarthome.automation.module.timer.test/META-INF/MANIFEST.MF index 3558ca1684f..562d66b649a 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.timer.test/META-INF/MANIFEST.MF +++ b/bundles/automation/org.eclipse.smarthome.automation.module.timer.test/META-INF/MANIFEST.MF @@ -17,7 +17,6 @@ Import-Package: org.eclipse.smarthome.automation.template, org.eclipse.smarthome.automation.type, org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.autoupdate, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.events, org.eclipse.smarthome.core.items, From dea5fb99e4b705e97500de22e6378fca07acf38c Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Wed, 31 Jan 2018 18:48:40 +0100 Subject: [PATCH 06/22] dropped AutoUpdateGenericBindingConfigProvider Signed-off-by: Simon Kaufmann --- .../META-INF/MANIFEST.MF | 1 - ...utoUpdateGenericBindingConfigProvider.java | 146 ------------------ .../META-INF/MANIFEST.MF | 1 - .../smarthome/test/java/JavaOSGiTest.java | 14 -- 4 files changed, 162 deletions(-) delete mode 100644 bundles/model/org.eclipse.smarthome.model.item/src/org/eclipse/smarthome/model/item/AutoUpdateGenericBindingConfigProvider.java diff --git a/bundles/model/org.eclipse.smarthome.model.item/META-INF/MANIFEST.MF b/bundles/model/org.eclipse.smarthome.model.item/META-INF/MANIFEST.MF index d9112368e52..0e0c2c9bb8e 100644 --- a/bundles/model/org.eclipse.smarthome.model.item/META-INF/MANIFEST.MF +++ b/bundles/model/org.eclipse.smarthome.model.item/META-INF/MANIFEST.MF @@ -25,7 +25,6 @@ Import-Package: org.apache.log4j, org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.autoupdate, org.eclipse.smarthome.core.common.registry, org.eclipse.smarthome.core.i18n, org.eclipse.smarthome.core.items, diff --git a/bundles/model/org.eclipse.smarthome.model.item/src/org/eclipse/smarthome/model/item/AutoUpdateGenericBindingConfigProvider.java b/bundles/model/org.eclipse.smarthome.model.item/src/org/eclipse/smarthome/model/item/AutoUpdateGenericBindingConfigProvider.java deleted file mode 100644 index 319dbbc7eca..00000000000 --- a/bundles/model/org.eclipse.smarthome.model.item/src/org/eclipse/smarthome/model/item/AutoUpdateGenericBindingConfigProvider.java +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * 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.eclipse.smarthome.model.item; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentHashMap; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.autoupdate.AutoUpdateBindingConfigProvider; -import org.osgi.service.component.annotations.Component; - -/** - *

- * This class can parse information from the generic binding format and provides AutoUpdate binding information from it. - * If no binding configuration is provided autoupdate is evaluated to true. This means every received - * Command will update its corresponding State by default. - * - *

- * This class registers as a {@link AutoUpdateBindingConfigProvider} service as well. - * - *

- * A valid binding configuration strings looks like this: - *

    - *
  • { autoupdate="false" }
  • - *
- * - * @author Thomas.Eichstaedt-Engelen - * @author Kai Kreuzer - made it independent from parent abstract classes - * - */ -@NonNullByDefault -@Component -public class AutoUpdateGenericBindingConfigProvider implements AutoUpdateBindingConfigProvider, BindingConfigReader { - - /** caches binding configurations. maps itemNames to {@link BindingConfig}s */ - protected Map bindingConfigs = new ConcurrentHashMap<>( - new WeakHashMap()); - - /** - * stores information about the context of items. The map has this content - * structure: context -> Set of Item names - */ - protected Map> contextMap = new ConcurrentHashMap<>(); - - @Override - public String getBindingType() { - return "autoupdate"; - } - - @Override - public void validateItemType(String itemType, String bindingConfig) throws BindingConfigParseException { - // as AutoUpdate is a default binding, each binding type is valid - } - - @Override - public void processBindingConfiguration(String context, String itemType, String itemName, String bindingConfig, - Configuration configuration) throws BindingConfigParseException { - Set itemNames = contextMap.get(context); - if (itemNames == null) { - itemNames = new HashSet(); - contextMap.put(context, itemNames); - } - itemNames.add(itemName); - - AutoUpdateBindingConfig config = new AutoUpdateBindingConfig(); - parseBindingConfig(bindingConfig, config); - addBindingConfig(itemType, itemName, config); - } - - protected void parseBindingConfig(String bindingConfig, AutoUpdateBindingConfig config) - throws BindingConfigParseException { - if (!bindingConfig.trim().isEmpty()) { - try { - config.autoupdate = Boolean.valueOf(bindingConfig.trim()); - } catch (IllegalArgumentException iae) { - throw new BindingConfigParseException("The given parameter '" + bindingConfig.trim() - + "' has to be set to either 'true' or 'false'."); - } - } - } - - @Override - public @Nullable Boolean autoUpdate(String itemName) { - AutoUpdateBindingConfig config = bindingConfigs.get(itemName); - return config != null ? config.autoupdate : null; - } - - @Override - public void startConfigurationUpdate(String context) { - Set itemNames = contextMap.get(context); - if (itemNames != null) { - for (String itemName : itemNames) { - // we remove all binding configurations for all items - bindingConfigs.remove(itemName); - } - contextMap.remove(context); - } - } - - @Override - public void stopConfigurationUpdate(String context) { - } - - protected void addBindingConfig(String itemType, String itemName, AutoUpdateBindingConfig config) { - bindingConfigs.put(itemName, config); - } - - public boolean providesBindingFor(String itemName) { - return bindingConfigs.get(itemName) != null; - } - - public boolean providesBinding() { - return !bindingConfigs.isEmpty(); - } - - public Collection getItemNames() { - return new ArrayList(bindingConfigs.keySet()); - } - - /** - * This is an internal data structure to store information from the binding - * config strings and use it to answer the requests to the AutoUpdate - * binding provider. - */ - static class AutoUpdateBindingConfig { - boolean autoupdate; - } - -} diff --git a/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF b/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF index e276c2593f4..f7aebff02f6 100644 --- a/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF +++ b/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF @@ -19,7 +19,6 @@ Import-Package: com.google.common.collect, org.codehaus.groovy.runtime.typehandling, org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.xml.osgi, - org.eclipse.smarthome.core.autoupdate, org.eclipse.smarthome.core.i18n;resolution:=optional, org.eclipse.smarthome.core.service, org.eclipse.smarthome.core.storage, diff --git a/bundles/test/org.eclipse.smarthome.test/src/main/java/org/eclipse/smarthome/test/java/JavaOSGiTest.java b/bundles/test/org.eclipse.smarthome.test/src/main/java/org/eclipse/smarthome/test/java/JavaOSGiTest.java index 5fb867b23bb..bd780bc1412 100644 --- a/bundles/test/org.eclipse.smarthome.test/src/main/java/org/eclipse/smarthome/test/java/JavaOSGiTest.java +++ b/bundles/test/org.eclipse.smarthome.test/src/main/java/org/eclipse/smarthome/test/java/JavaOSGiTest.java @@ -24,7 +24,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.autoupdate.AutoUpdateBindingConfigProvider; import org.eclipse.smarthome.test.internal.java.MissingServiceAnalyzer; import org.eclipse.smarthome.test.storage.VolatileStorageService; import org.junit.After; @@ -311,17 +310,4 @@ public void unregisterMocks() { registeredServices.clear(); } - /** - * Inject a service to disable the auto-update feature. - */ - protected void disableItemAutoUpdate() { - registerService(new AutoUpdateBindingConfigProvider() { - - @Override - public @Nullable Boolean autoUpdate(String itemName) { - return false; - } - }); - } - } From ae6e964422e03545b6757b99c1330d6f09d6c943 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Thu, 1 Feb 2018 13:14:20 +0100 Subject: [PATCH 07/22] adapt sitemap subscribers Signed-off-by: Simon Kaufmann --- .../sitemap/SitemapSubscriptionService.java | 19 ++++++- .../sitemap/internal/PageChangeListener.java | 53 +++++++++++++------ .../ui/internal/items/ItemUIRegistryImpl.java | 11 ++-- .../smarthome/ui/items/ItemUIRegistry.java | 50 ++++++++++++----- 4 files changed, 100 insertions(+), 33 deletions(-) diff --git a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java index a825e0ef3cf..714abb6375a 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java @@ -23,6 +23,9 @@ import org.apache.commons.lang.StringUtils; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.EList; +import org.eclipse.smarthome.core.items.CommandResultPredictionListener; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.io.rest.sitemap.internal.PageChangeListener; import org.eclipse.smarthome.io.rest.sitemap.internal.SitemapEvent; import org.eclipse.smarthome.model.core.EventType; @@ -53,7 +56,7 @@ * @author Kai Kreuzer - Initial contribution and API */ @Component(service = SitemapSubscriptionService.class, configurationPid = "org.eclipse.smarthome.sitemapsubscription") -public class SitemapSubscriptionService implements ModelRepositoryChangeListener { +public class SitemapSubscriptionService implements ModelRepositoryChangeListener, CommandResultPredictionListener { private static final String SITEMAP_PAGE_SEPARATOR = "#"; private static final String SITEMAP_SUFFIX = ".sitemap"; @@ -339,4 +342,18 @@ public void checkAliveClients() { listenerEntry.getValue().sendAliveEvent(); } } + + @Override + public void keepCurrentState(Item item) { + for (PageChangeListener pageChangeListener : pageChangeListeners.values()) { + pageChangeListener.keepCurrentState(item); + } + } + + @Override + public void changeStateTo(Item item, State state) { + for (PageChangeListener pageChangeListener : pageChangeListeners.values()) { + pageChangeListener.changeStateTo(item, state); + } + } } diff --git a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java index 52d56c91737..adc8b482cdf 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java @@ -17,9 +17,13 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import org.eclipse.emf.common.util.EList; +import org.eclipse.smarthome.core.common.ThreadPoolManager; +import org.eclipse.smarthome.core.items.CommandResultPredictionListener; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.items.GroupItem; import org.eclipse.smarthome.core.items.Item; @@ -42,7 +46,7 @@ * @author Kai Kreuzer - Initial contribution and API * */ -public class PageChangeListener implements StateChangeListener { +public class PageChangeListener implements StateChangeListener, CommandResultPredictionListener { private final String sitemapName; private final String pageId; @@ -164,18 +168,22 @@ private void addItemWithName(Set items, String itemName) { } } + private void constructAndSendEvents(Item item, State newState) { + Set events = constructSitemapEvents(item, newState, widgets); + for (SitemapEvent event : events) { + for (SitemapSubscriptionCallback callback : distinctCallbacks) { + callback.onEvent(event); + } + } + } + @Override public void stateChanged(Item item, State oldState, State newState) { // For all items except group, send an event only when the event state is changed. if (item instanceof GroupItem) { return; } - Set events = constructSitemapEvents(item, widgets); - for (SitemapEvent event : events) { - for (SitemapSubscriptionCallback callback : distinctCallbacks) { - callback.onEvent(event); - } - } + constructAndSendEvents(item, newState); } @Override @@ -186,19 +194,34 @@ public void stateUpdated(Item item, State state) { if (!(item instanceof GroupItem)) { return; } - Set events = constructSitemapEvents(item, widgets); - for (SitemapEvent event : events) { - for (SitemapSubscriptionCallback callback : distinctCallbacks) { - callback.onEvent(event); - } + constructAndSendEvents(item, state); + } + + @Override + public void keepCurrentState(Item item) { + if (item instanceof GroupItem) { + return; + } + scheduler.schedule(() -> { + constructAndSendEvents(item, item.getState()); + }, 200, TimeUnit.MILLISECONDS); + } + + private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("ui"); + + @Override + public void changeStateTo(Item item, State state) { + if (item instanceof GroupItem) { + return; } + constructAndSendEvents(item, state); } - private Set constructSitemapEvents(Item item, List widgets) { + private Set constructSitemapEvents(Item item, State state, List widgets) { Set events = new HashSet<>(); for (Widget w : widgets) { if (w instanceof Frame) { - events.addAll(constructSitemapEvents(item, itemUIRegistry.getChildren((Frame) w))); + events.addAll(constructSitemapEvents(item, state, itemUIRegistry.getChildren((Frame) w))); } boolean skipWidget = (w.getItem() == null) || !w.getItem().equals(item.getName()); @@ -224,7 +247,7 @@ private Set constructSitemapEvents(Item item, List widgets event.item = EnrichedItemDTOMapper.map(item, drillDown, itemFilter, null, null); // event.state is an adjustment of the item state to the widget type. - event.state = itemUIRegistry.getState(w).toFullString(); + event.state = itemUIRegistry.convertState(w, item, state).toFullString(); // In case this state is identical to the item state, its value is set to null. if (event.state != null && event.state.equals(event.item.state)) { event.state = null; diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImpl.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImpl.java index 5098c2be3a8..78e274270e1 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImpl.java +++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/internal/items/ItemUIRegistryImpl.java @@ -33,6 +33,7 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; import org.eclipse.smarthome.core.items.GroupItem; import org.eclipse.smarthome.core.items.Item; @@ -568,7 +569,7 @@ public State getState(Widget w) { if (itemName != null) { try { Item item = getItem(itemName); - return convertState(w, item); + return convertState(w, item, item.getState()); } catch (ItemNotFoundException e) { logger.error("Cannot retrieve item '{}' for widget {}", new Object[] { itemName, w.eClass().getInstanceTypeName() }); @@ -582,9 +583,11 @@ public State getState(Widget w) { * * @param w Widget in sitemap that shows the state * @param i item + * @param state * @return the converted state or the original if conversion was not possible */ - private State convertState(Widget w, Item i) { + @Override + public State convertState(Widget w, Item i, State state) { State returnState = null; State itemState = i.getState(); @@ -1290,7 +1293,7 @@ public void removeRegistryHook(RegistryHook hook) { } @Override - public String getUnitForWidget(Widget w) { + public @Nullable String getUnitForWidget(Widget w) { try { Item item = getItem(w.getItem()); @@ -1311,7 +1314,7 @@ public String getUnitForWidget(Widget w) { } @Override - public State convertStateToLabelUnit(QuantityType state, String label) { + public @Nullable State convertStateToLabelUnit(QuantityType state, String label) { String labelUnit = label.lastIndexOf(" ") > 0 ? label.substring(label.lastIndexOf(" ")) : null; if (labelUnit != null && !state.getUnit().toString().equals(labelUnit)) { return state.toUnit(labelUnit); diff --git a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIRegistry.java b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIRegistry.java index f827fe41dc6..db0207018e7 100644 --- a/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIRegistry.java +++ b/bundles/ui/org.eclipse.smarthome.ui/src/main/java/org/eclipse/smarthome/ui/items/ItemUIRegistry.java @@ -16,7 +16,9 @@ import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.items.Item; import org.eclipse.smarthome.core.items.ItemRegistry; import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.types.State; @@ -33,6 +35,7 @@ * @author Chris Jackson * */ +@NonNullByDefault public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { /** @@ -49,7 +52,8 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param w the widget to retrieve the label for * @return the label to use for the widget */ - String getLabel(@NonNull Widget w); + @Nullable + String getLabel(Widget w); /** * Retrieves the category for a widget. @@ -61,7 +65,8 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param w the widget to retrieve the category for * @return the category to use for the widget */ - String getCategory(@NonNull Widget w); + @Nullable + String getCategory(Widget w); /** * Retrieves the current state of the item of a widget or UnDefType.UNDEF. @@ -69,7 +74,8 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param w the widget to retrieve the item state for * @return the item state of the widget */ - State getState(@NonNull Widget w); + @Nullable + State getState(Widget w); /** * Retrieves the widget for a given id on a given sitemap. @@ -78,7 +84,8 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param id the id of the widget to look for * @return the widget for the given id */ - Widget getWidget(@NonNull Sitemap sitemap, @NonNull String id); + @Nullable + Widget getWidget(Sitemap sitemap, String id); /** * Provides an id for a widget. @@ -92,7 +99,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param w the widget to get the id for * @return an id for this widget */ - String getWidgetId(@NonNull Widget w); + String getWidgetId(Widget w); /** * this should be used instead of Sitemap.getChildren() as the default @@ -101,7 +108,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param w the sitemap to retrieve the children for * @return the children of the sitemap */ - EList getChildren(@NonNull Sitemap sitemap); + EList getChildren(Sitemap sitemap); /** * this should be used instead of LinkableWidget.getChildren() as there @@ -111,7 +118,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param w the widget to retrieve the children for * @return the (dynamically or statically defined) children of the widget */ - EList getChildren(@NonNull LinkableWidget w); + EList getChildren(LinkableWidget w); /** * this should be used instead of Widget.eContainer() as as the concrete @@ -120,7 +127,8 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param w the widget to retrieve the parent for * @return the parent of the widget */ - EObject getParent(@NonNull Widget w); + @Nullable + EObject getParent(Widget w); /** * Gets the label color for the widget. Checks conditional statements to @@ -129,7 +137,8 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param w Widget * @return String with the color */ - String getLabelColor(@NonNull Widget w); + @Nullable + String getLabelColor(Widget w); /** * Gets the value color for the widget. Checks conditional statements to @@ -138,7 +147,8 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param w Widget * @return String with the color */ - String getValueColor(@NonNull Widget w); + @Nullable + String getValueColor(Widget w); /** * Gets the widget visibility based on the item state @@ -146,7 +156,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param w Widget * @return true if the item is visible */ - boolean getVisiblity(@NonNull Widget w); + boolean getVisiblity(Widget w); /** * Gets the item state @@ -154,7 +164,8 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param itemName item name * @return State of the item */ - State getItemState(@NonNull String itemName); + @Nullable + State getItemState(String itemName); /** * Provide a widget specific String representation of a {@link Unit}. @@ -162,6 +173,7 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param widget * @return a widget specific String representation of a {@link Unit}. */ + @Nullable String getUnitForWidget(Widget widget); /** @@ -172,6 +184,18 @@ public interface ItemUIRegistry extends ItemRegistry, ItemUIProvider { * @param label the label containing the target unit. * @return the converted state. */ + @Nullable State convertStateToLabelUnit(QuantityType state, String label); + /** + * Convert the given state into + * + * @param widget Widget + * @param item item + * @param state state + * @return the state converted to a type accepted by the item or the given state if the conversion was not possible + */ + @Nullable + State convertState(Widget widget, Item item, State state); + } From 33354448c53efa9d67ec38645f335c7161335a52 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Thu, 1 Feb 2018 14:03:30 +0100 Subject: [PATCH 08/22] adapted build Signed-off-by: Simon Kaufmann --- bundles/core/pom.xml | 1 - features/karaf/esh-core/src/main/feature/feature.xml | 1 - .../org.eclipse.smarthome.feature.runtime.core/feature.xml | 7 ------- 3 files changed, 9 deletions(-) diff --git a/bundles/core/pom.xml b/bundles/core/pom.xml index 86b3e0be3a1..6049e149589 100644 --- a/bundles/core/pom.xml +++ b/bundles/core/pom.xml @@ -19,7 +19,6 @@ org.eclipse.smarthome.core org.eclipse.smarthome.core.audio org.eclipse.smarthome.core.audio.test - org.eclipse.smarthome.core.autoupdate org.eclipse.smarthome.core.extension.sample org.eclipse.smarthome.core.id org.eclipse.smarthome.core.id.test diff --git a/features/karaf/esh-core/src/main/feature/feature.xml b/features/karaf/esh-core/src/main/feature/feature.xml index a1e19b80ce6..0e1542250a6 100644 --- a/features/karaf/esh-core/src/main/feature/feature.xml +++ b/features/karaf/esh-core/src/main/feature/feature.xml @@ -70,7 +70,6 @@ osgi.service;objectClass=org.eclipse.smarthome.core.items.ManagedItemProvider - mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.autoupdate/${project.version} mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.binding.xml/${project.version} mvn:org.eclipse.smarthome.core/org.eclipse.smarthome.core.id/${project.version} diff --git a/features/org.eclipse.smarthome.feature.runtime.core/feature.xml b/features/org.eclipse.smarthome.feature.runtime.core/feature.xml index 4763517164a..3689ce0a1e2 100644 --- a/features/org.eclipse.smarthome.feature.runtime.core/feature.xml +++ b/features/org.eclipse.smarthome.feature.runtime.core/feature.xml @@ -115,13 +115,6 @@ version="0.0.0" unpack="false"/> - - Date: Fri, 2 Feb 2018 12:54:19 +0100 Subject: [PATCH 09/22] removed autoupdate bundle from test setups Signed-off-by: Simon Kaufmann --- .../pom.xml | 10 ---------- .../pom.xml | 10 ---------- .../pom.xml | 10 ---------- .../pom.xml | 10 ---------- .../pom.xml | 10 ---------- .../core/org.eclipse.smarthome.core.thing.test/pom.xml | 5 ----- 6 files changed, 55 deletions(-) diff --git a/bundles/automation/org.eclipse.smarthome.automation.event.test/pom.xml b/bundles/automation/org.eclipse.smarthome.automation.event.test/pom.xml index 3ff473f8a82..51704fff2a3 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.event.test/pom.xml +++ b/bundles/automation/org.eclipse.smarthome.automation.event.test/pom.xml @@ -59,11 +59,6 @@ org.eclipse.osgi 0.0.0 - - eclipse-plugin - org.eclipse.smarthome.core.autoupdate - 0.0.0 - eclipse-plugin @@ -142,11 +137,6 @@ 4 true - - org.eclipse.smarthome.core.autoupdate - 2 - true - diff --git a/bundles/automation/org.eclipse.smarthome.automation.integration.test/pom.xml b/bundles/automation/org.eclipse.smarthome.automation.integration.test/pom.xml index ab401914f90..a84e5ddae08 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.integration.test/pom.xml +++ b/bundles/automation/org.eclipse.smarthome.automation.integration.test/pom.xml @@ -59,11 +59,6 @@ org.eclipse.osgi 0.0.0 - - eclipse-plugin - org.eclipse.smarthome.core.autoupdate - 0.0.0 - eclipse-plugin @@ -148,11 +143,6 @@ 4 true - - org.eclipse.smarthome.core.autoupdate - 2 - true - diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.core.test/pom.xml b/bundles/automation/org.eclipse.smarthome.automation.module.core.test/pom.xml index 2e7c4ba755b..31547b488c1 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.core.test/pom.xml +++ b/bundles/automation/org.eclipse.smarthome.automation.module.core.test/pom.xml @@ -64,11 +64,6 @@ org.eclipse.osgi 0.0.0 - - eclipse-plugin - org.eclipse.smarthome.core.autoupdate - 0.0.0 - eclipse-plugin @@ -147,11 +142,6 @@ 4 true - - org.eclipse.smarthome.core.autoupdate - 2 - true - diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.script.test/pom.xml b/bundles/automation/org.eclipse.smarthome.automation.module.script.test/pom.xml index e6310d8255d..0455151c8bc 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.script.test/pom.xml +++ b/bundles/automation/org.eclipse.smarthome.automation.module.script.test/pom.xml @@ -66,11 +66,6 @@ org.eclipse.osgi 0.0.0 - - eclipse-plugin - org.eclipse.smarthome.core.autoupdate - 0.0.0 - eclipse-plugin org.eclipse.smarthome.automation.api @@ -175,11 +170,6 @@ 4 true - - org.eclipse.smarthome.core.autoupdate - 4 - true - org.eclipse.smarthome.core.thing 4 diff --git a/bundles/automation/org.eclipse.smarthome.automation.module.timer.test/pom.xml b/bundles/automation/org.eclipse.smarthome.automation.module.timer.test/pom.xml index 2d217de9644..d688e9e0df3 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.module.timer.test/pom.xml +++ b/bundles/automation/org.eclipse.smarthome.automation.module.timer.test/pom.xml @@ -67,11 +67,6 @@ org.eclipse.osgi 0.0.0 - - eclipse-plugin - org.eclipse.smarthome.core.autoupdate - 0.0.0 - eclipse-plugin @@ -162,11 +157,6 @@ 4 true - - org.eclipse.smarthome.core.autoupdate - 2 - true - org.eclipse.smarthome.core.scheduler 2 diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/pom.xml b/bundles/core/org.eclipse.smarthome.core.thing.test/pom.xml index 029746600cc..385059574af 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/pom.xml +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/pom.xml @@ -104,11 +104,6 @@ 4 true - - org.eclipse.smarthome.core.autoupdate - 4 - true - From 4e0796d74cdcd8b5e681f76a2ddb82cfc9e47eda Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Tue, 24 Apr 2018 16:30:55 +0200 Subject: [PATCH 10/22] sending prediction event Signed-off-by: Simon Kaufmann --- .../thing/internal/AutoUpdateManagerTest.java | 19 ++++- .../thing/internal/AutoUpdateManager.java | 20 ++++-- .../core/items/events/ItemEventFactory.java | 68 +++++++++++++++++- .../items/events/ItemStatePredictedEvent.java | 71 +++++++++++++++++++ 4 files changed, 169 insertions(+), 9 deletions(-) create mode 100644 bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemStatePredictedEvent.java diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java index a028907c67b..1e1b4540e34 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java @@ -1,7 +1,19 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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.eclipse.smarthome.core.thing.internal; import static org.junit.Assert.*; -import static org.mockito.Matchers.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; @@ -34,6 +46,11 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; +/** + * + * @author Simon Kaufmann - initial contribution and API + * + */ public class AutoUpdateManagerTest { private static final ThingUID THING_UID_ONLINE = new ThingUID("test::mock"); diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java index 2583bfca78f..ebe7ce0ed0c 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java @@ -45,6 +45,7 @@ import org.slf4j.LoggerFactory; /** + * Component which takes care of calculating and sending potential auto-update event. * * @author Simon Kaufmann - initial contribution and API * @@ -145,9 +146,7 @@ public void receiveCommand(ItemCommandEvent commandEvent, Item item) { break; case OPTIMISTIC: logger.trace("Optimistically updating item '{}'", itemName); - for (CommandResultPredictionListener listener : commandResultPredictionListeners) { - listener.changeStateTo(item, state); - } + postPrediction(item, state, false); if (sendOptimisticUpdates) { postUpdate(item, state, EVENT_SOURCE_OPTIMISTIC); } @@ -157,9 +156,7 @@ public void receiveCommand(ItemCommandEvent commandEvent, Item item) { break; case REVERT: logger.trace("Sending current item state to revert controls '{}'", itemName); - for (CommandResultPredictionListener listener : commandResultPredictionListeners) { - listener.keepCurrentState(item); - } + postPrediction(item, item.getState(), true); break; } } @@ -219,6 +216,17 @@ private void postUpdate(Item item, State newState, String origin) { } } + private void postPrediction(Item item, State predictedState, boolean isConfirmation) { + boolean isAccepted = isAcceptedState(predictedState, item); + if (isAccepted) { + eventPublisher + .post(ItemEventFactory.createStatePredictedEvent(item.getName(), predictedState, isConfirmation)); + } else { + logger.debug("Received prediction of a not accepted type ({}) for item {}", + predictedState.getClass().getSimpleName(), item.getName()); + } + } + private boolean isAcceptedState(State newState, Item item) { boolean isAccepted = false; if (item.getAcceptedDataTypes().contains(newState.getClass())) { diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemEventFactory.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemEventFactory.java index 920bd1662d3..5b82ba6c07c 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemEventFactory.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemEventFactory.java @@ -50,6 +50,8 @@ public class ItemEventFactory extends AbstractEventFactory { private static final String ITEM_STATE_EVENT_TOPIC = "smarthome/items/{itemName}/state"; + private static final String ITEM_STATE_PREDICTED_EVENT_TOPIC = "smarthome/items/{itemName}/statepredicted"; + private static final String ITEM_STATE_CHANGED_EVENT_TOPIC = "smarthome/items/{itemName}/statechanged"; private static final String GROUPITEM_STATE_CHANGED_EVENT_TOPIC = "smarthome/items/{itemName}/{memberName}/statechanged"; @@ -64,8 +66,9 @@ public class ItemEventFactory extends AbstractEventFactory { * Constructs a new ItemEventFactory. */ public ItemEventFactory() { - super(Sets.newHashSet(ItemCommandEvent.TYPE, ItemStateEvent.TYPE, ItemStateChangedEvent.TYPE, - ItemAddedEvent.TYPE, ItemUpdatedEvent.TYPE, ItemRemovedEvent.TYPE, GroupItemStateChangedEvent.TYPE)); + super(Sets.newHashSet(ItemCommandEvent.TYPE, ItemStateEvent.TYPE, ItemStatePredictedEvent.TYPE, + ItemStateChangedEvent.TYPE, ItemAddedEvent.TYPE, ItemUpdatedEvent.TYPE, ItemRemovedEvent.TYPE, + GroupItemStateChangedEvent.TYPE)); } @Override @@ -75,6 +78,8 @@ protected Event createEventByType(String eventType, String topic, String payload event = createCommandEvent(topic, payload, source); } else if (eventType.equals(ItemStateEvent.TYPE)) { event = createStateEvent(topic, payload, source); + } else if (eventType.equals(ItemStatePredictedEvent.TYPE)) { + event = createStatePredictedEvent(topic, payload, source); } else if (eventType.equals(ItemStateChangedEvent.TYPE)) { event = createStateChangedEvent(topic, payload); } else if (eventType.equals(ItemAddedEvent.TYPE)) { @@ -112,6 +117,13 @@ private Event createStateEvent(String topic, String payload, String source) { return new ItemStateEvent(topic, payload, itemName, state, source); } + private Event createStatePredictedEvent(String topic, String payload, String source) { + String itemName = getItemName(topic); + ItemStatePredictedEventPayloadBean bean = deserializePayload(payload, ItemStatePredictedEventPayloadBean.class); + State state = getState(bean.getPredictedType(), bean.getPredictedValue()); + return new ItemStatePredictedEvent(topic, payload, itemName, state, bean.isConfirmation()); + } + private Event createStateChangedEvent(String topic, String payload) { String itemName = getItemName(topic); ItemStateChangedEventPayloadBean bean = deserializePayload(payload, ItemStateChangedEventPayloadBean.class); @@ -256,6 +268,24 @@ public static ItemStateEvent createStateEvent(String itemName, State state) { return createStateEvent(itemName, state, null); } + /** + * Creates an item state predicted event. + * + * @param itemName the name of the item to send the state update for + * @param state the predicted state to send + * @param isConfirmation whether this is a confirmation of a previous state + * @return the created item state predicted event + * @throws IllegalArgumentException if itemName or state is null + */ + public static ItemStatePredictedEvent createStatePredictedEvent(String itemName, State state, + boolean isConfirmation) { + assertValidArguments(itemName, state, "state"); + String topic = buildTopic(ITEM_STATE_PREDICTED_EVENT_TOPIC, itemName); + ItemEventPayloadBean bean = new ItemEventPayloadBean(getStateType(state), state.toFullString()); + String payload = serializePayload(bean); + return new ItemStatePredictedEvent(topic, payload, itemName, state, isConfirmation); + } + /** * Creates an item state changed event. * @@ -398,6 +428,40 @@ public String getValue() { } } + /** + * This is a java bean that is used to serialize/deserialize item state changed event payload. + */ + private static class ItemStatePredictedEventPayloadBean { + private String predictedType; + private String predictedValue; + private boolean isConfirmation; + + /** + * Default constructor for deserialization e.g. by Gson. + */ + @SuppressWarnings("unused") + protected ItemStatePredictedEventPayloadBean() { + } + + public ItemStatePredictedEventPayloadBean(String predictedType, String predictedValue, boolean isConfirmation) { + this.predictedType = predictedType; + this.predictedValue = predictedValue; + this.isConfirmation = isConfirmation; + } + + public String getPredictedType() { + return predictedType; + } + + public String getPredictedValue() { + return predictedValue; + } + + public boolean isConfirmation() { + return isConfirmation; + } + } + /** * This is a java bean that is used to serialize/deserialize item state changed event payload. */ diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemStatePredictedEvent.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemStatePredictedEvent.java new file mode 100644 index 00000000000..22c3f66d685 --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemStatePredictedEvent.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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.eclipse.smarthome.core.items.events; + +import org.eclipse.smarthome.core.events.AbstractEvent; +import org.eclipse.smarthome.core.types.State; + +/** + * This event announces potential item state outcomes when a command was received. + *

+ * Thereby it denotes that the item state is most likely going to change to the given predicted value. + *

+ * If {@code isConfirmation == true}, then it basically only confirms the previous item state because a received command + * will not be successfully executed and therefore presumably will not result in a state change (e.g. because no handler + * currently is capable of delivering such an event to its device). + * + * @author Simon Kaufmann - initial contribution and API + * + */ +public class ItemStatePredictedEvent extends AbstractEvent { + + /** + * The item state changed event type. + */ + public static final String TYPE = ItemStatePredictedEvent.class.getSimpleName(); + + protected final String itemName; + protected final State predictedState; + protected final boolean isConfirmation; + + public ItemStatePredictedEvent(String topic, String payload, String itemName, State predictedState, + boolean isConfirmation) { + super(topic, payload, null); + this.itemName = itemName; + this.predictedState = predictedState; + this.isConfirmation = isConfirmation; + } + + @Override + public String getType() { + return TYPE; + } + + public String getItemName() { + return itemName; + } + + public State getPredictedState() { + return predictedState; + } + + public boolean isConfirmation() { + return isConfirmation; + } + + @Override + public String toString() { + return String.format("%s predicted to become %s", itemName, predictedState); + } + +} From 297e51f94032e4b1b39420a38e4d062723b39847 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Fri, 22 Jun 2018 14:03:24 +0200 Subject: [PATCH 11/22] sitemap using the state prediction events also Signed-off-by: Simon Kaufmann --- .../META-INF/MANIFEST.MF | 3 ++ .../sitemap/SitemapSubscriptionService.java | 43 ++++++++++++++----- .../sitemap/internal/PageChangeListener.java | 14 +----- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/META-INF/MANIFEST.MF b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/META-INF/MANIFEST.MF index 786239a0e8e..93e8709a406 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/META-INF/MANIFEST.MF +++ b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/META-INF/MANIFEST.MF @@ -21,8 +21,11 @@ Import-Package: org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.auth, org.eclipse.smarthome.core.common, + org.eclipse.smarthome.core.common.registry, + org.eclipse.smarthome.core.events, org.eclipse.smarthome.core.items, org.eclipse.smarthome.core.items.dto, + org.eclipse.smarthome.core.items.events, org.eclipse.smarthome.core.library, org.eclipse.smarthome.core.types, org.eclipse.smarthome.io.rest, diff --git a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java index 714abb6375a..ac4c49acd91 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java @@ -13,19 +13,25 @@ package org.eclipse.smarthome.io.rest.sitemap; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang.StringUtils; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.EList; -import org.eclipse.smarthome.core.items.CommandResultPredictionListener; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.events.Event; +import org.eclipse.smarthome.core.events.EventFilter; +import org.eclipse.smarthome.core.events.EventSubscriber; +import org.eclipse.smarthome.core.items.GroupItem; import org.eclipse.smarthome.core.items.Item; -import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.items.events.ItemStatePredictedEvent; import org.eclipse.smarthome.io.rest.sitemap.internal.PageChangeListener; import org.eclipse.smarthome.io.rest.sitemap.internal.SitemapEvent; import org.eclipse.smarthome.model.core.EventType; @@ -55,8 +61,9 @@ * * @author Kai Kreuzer - Initial contribution and API */ -@Component(service = SitemapSubscriptionService.class, configurationPid = "org.eclipse.smarthome.sitemapsubscription") -public class SitemapSubscriptionService implements ModelRepositoryChangeListener, CommandResultPredictionListener { +@Component(service = { SitemapSubscriptionService.class, + EventSubscriber.class }, configurationPid = "org.eclipse.smarthome.sitemapsubscription") +public class SitemapSubscriptionService implements ModelRepositoryChangeListener, EventSubscriber { private static final String SITEMAP_PAGE_SEPARATOR = "#"; private static final String SITEMAP_SUFFIX = ".sitemap"; @@ -344,16 +351,30 @@ public void checkAliveClients() { } @Override - public void keepCurrentState(Item item) { - for (PageChangeListener pageChangeListener : pageChangeListeners.values()) { - pageChangeListener.keepCurrentState(item); - } + public Set getSubscribedEventTypes() { + return Collections.singleton(ItemStatePredictedEvent.TYPE); + } + + @Override + public @Nullable EventFilter getEventFilter() { + return null; } @Override - public void changeStateTo(Item item, State state) { - for (PageChangeListener pageChangeListener : pageChangeListeners.values()) { - pageChangeListener.changeStateTo(item, state); + public void receive(Event event) { + if (event instanceof ItemStatePredictedEvent) { + ItemStatePredictedEvent prediction = (ItemStatePredictedEvent) event; + Item item = itemUIRegistry.get(prediction.getItemName()); + if (item instanceof GroupItem) { + return; + } + for (PageChangeListener pageChangeListener : pageChangeListeners.values()) { + if (prediction.isConfirmation()) { + pageChangeListener.keepCurrentState(item); + } else { + pageChangeListener.changeStateTo(item, prediction.getPredictedState()); + } + } } } } diff --git a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java index adc8b482cdf..492e6463d48 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java @@ -23,7 +23,6 @@ import org.eclipse.emf.common.util.EList; import org.eclipse.smarthome.core.common.ThreadPoolManager; -import org.eclipse.smarthome.core.items.CommandResultPredictionListener; import org.eclipse.smarthome.core.items.GenericItem; import org.eclipse.smarthome.core.items.GroupItem; import org.eclipse.smarthome.core.items.Item; @@ -46,8 +45,9 @@ * @author Kai Kreuzer - Initial contribution and API * */ -public class PageChangeListener implements StateChangeListener, CommandResultPredictionListener { +public class PageChangeListener implements StateChangeListener { + private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("ui"); private final String sitemapName; private final String pageId; private final ItemUIRegistry itemUIRegistry; @@ -197,23 +197,13 @@ public void stateUpdated(Item item, State state) { constructAndSendEvents(item, state); } - @Override public void keepCurrentState(Item item) { - if (item instanceof GroupItem) { - return; - } scheduler.schedule(() -> { constructAndSendEvents(item, item.getState()); }, 200, TimeUnit.MILLISECONDS); } - private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("ui"); - - @Override public void changeStateTo(Item item, State state) { - if (item instanceof GroupItem) { - return; - } constructAndSendEvents(item, state); } From 7ee264f9bdc6ff9d6127d55d9ec274aa4034fb70 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Fri, 22 Jun 2018 14:04:02 +0200 Subject: [PATCH 12/22] dropping teh command result prediction listener interface again Signed-off-by: Simon Kaufmann --- .../thing/internal/AutoUpdateManager.java | 14 ------ .../CommandResultPredictionListener.java | 47 ------------------- 2 files changed, 61 deletions(-) delete mode 100644 bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/CommandResultPredictionListener.java diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java index ebe7ce0ed0c..2e0c1f13ccc 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java @@ -14,7 +14,6 @@ import static java.util.stream.Collectors.toSet; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -22,7 +21,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.events.EventPublisher; -import org.eclipse.smarthome.core.items.CommandResultPredictionListener; import org.eclipse.smarthome.core.items.Item; import org.eclipse.smarthome.core.items.events.ItemCommandEvent; import org.eclipse.smarthome.core.items.events.ItemEventFactory; @@ -39,8 +37,6 @@ import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.Modified; 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; @@ -66,7 +62,6 @@ public class AutoUpdateManager { private @NonNullByDefault({}) ItemChannelLinkRegistry itemChannelLinkRegistry; private @NonNullByDefault({}) ThingRegistry thingRegistry; private @NonNullByDefault({}) EventPublisher eventPublisher; - private final Set commandResultPredictionListeners = new HashSet<>(); private final Map autoUpdatePolicies = new ConcurrentHashMap<>(); @@ -280,13 +275,4 @@ protected void unsetEventPublisher(EventPublisher eventPublisher) { this.eventPublisher = null; } - @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) - protected void addStateRevertListener(CommandResultPredictionListener commandResultPredictionListener) { - commandResultPredictionListeners.add(commandResultPredictionListener); - } - - protected void removeStateRevertListener(CommandResultPredictionListener commandResultPredictionListener) { - commandResultPredictionListeners.remove(commandResultPredictionListener); - } - } diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/CommandResultPredictionListener.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/CommandResultPredictionListener.java deleted file mode 100644 index 1fe03386faf..00000000000 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/CommandResultPredictionListener.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * 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.eclipse.smarthome.core.items; - -import org.eclipse.smarthome.core.types.State; - -/** - * Interface which announces potential item state outcomes when a command was received. - *

- * This interface is intended to be implemented only within the framework itself and not meant to be implemented by - * bindings. - * - * @author Simon Kaufmann - initial contribution and API - * - */ -public interface CommandResultPredictionListener { - - /** - * Denotes that the item state is most likely going to change to the given value. - * - * @param item - * @param state - */ - void changeStateTo(Item item, State state); - - /** - * Reconfirms the previous item state because a command for sure will not be successfully executed. - *

- * This is actually the exact opposite of {@link #changeStateTo(Item, State)}: it denotes that a recently received - * command presumably will not result in a state change, e.g. because no handler currently is capable of delivering - * such an event to its device. - * - * @param item - */ - void keepCurrentState(Item item); - -} From 17d76747dc2682c914953b209995c20e60d2c101 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Fri, 22 Jun 2018 14:55:21 +0200 Subject: [PATCH 13/22] policy now at channel, auto update manager using it Signed-off-by: Simon Kaufmann --- .../thing/internal/AutoUpdateManagerTest.java | 71 +++++++++++-------- .../eclipse/smarthome/core/thing/Channel.java | 22 ++++-- .../thing/binding/ThingHandlerCallback.java | 8 --- .../thing/binding/builder/ChannelBuilder.java | 19 ++++- .../thing/internal/AutoUpdateManager.java | 11 +-- .../core/thing/internal/ThingManager.java | 7 -- .../{binding => type}/AutoUpdatePolicy.java | 2 +- 7 files changed, 82 insertions(+), 58 deletions(-) rename bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/{binding => type}/AutoUpdatePolicy.java (96%) diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java index 1e1b4540e34..045caaa9a30 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java @@ -13,22 +13,23 @@ package org.eclipse.smarthome.core.thing.internal; import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.eclipse.smarthome.core.events.Event; import org.eclipse.smarthome.core.events.EventPublisher; -import org.eclipse.smarthome.core.items.CommandResultPredictionListener; import org.eclipse.smarthome.core.items.GenericItem; -import org.eclipse.smarthome.core.items.Item; import org.eclipse.smarthome.core.items.events.ItemCommandEvent; import org.eclipse.smarthome.core.items.events.ItemEventFactory; import org.eclipse.smarthome.core.items.events.ItemStateEvent; +import org.eclipse.smarthome.core.items.events.ItemStatePredictedEvent; import org.eclipse.smarthome.core.library.items.StringItem; import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.thing.ChannelUID; @@ -36,11 +37,11 @@ import org.eclipse.smarthome.core.thing.ThingRegistry; import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingUID; -import org.eclipse.smarthome.core.thing.binding.AutoUpdatePolicy; import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; import org.eclipse.smarthome.core.thing.link.ItemChannelLink; import org.eclipse.smarthome.core.thing.link.ItemChannelLinkRegistry; +import org.eclipse.smarthome.core.thing.type.AutoUpdatePolicy; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -67,10 +68,10 @@ public class AutoUpdateManagerTest { private @Mock Thing mockThingOnline; private @Mock Thing mockThingHandlerMissing; private @Mock ThingHandler mockHandler; - private @Mock CommandResultPredictionListener mockPredictionListener; private final List links = new LinkedList<>(); private AutoUpdateManager aum; + private final Map policies = new HashMap<>(); @Before public void setup() { @@ -84,15 +85,16 @@ public void setup() { when(mockThingRegistry.get(eq(THING_UID_HANDLER_MISSING))).thenReturn(mockThingHandlerMissing); when(mockThingOnline.getHandler()).thenReturn(mockHandler); when(mockThingOnline.getChannel(eq(CHANNEL_UID_ONLINE_1.getId()))) - .thenReturn(ChannelBuilder.create(CHANNEL_UID_ONLINE_1, "String").build()); + .thenAnswer(answer -> ChannelBuilder.create(CHANNEL_UID_ONLINE_1, "String") + .withAutoUpdatePolicy(policies.get(CHANNEL_UID_ONLINE_1)).build()); when(mockThingOnline.getChannel(eq(CHANNEL_UID_ONLINE_2.getId()))) - .thenReturn(ChannelBuilder.create(CHANNEL_UID_ONLINE_2, "String").build()); + .thenAnswer(answer -> ChannelBuilder.create(CHANNEL_UID_ONLINE_2, "String") + .withAutoUpdatePolicy(policies.get(CHANNEL_UID_ONLINE_2)).build()); aum = new AutoUpdateManager(); aum.setItemChannelLinkRegistry(mockLinkRegistry); aum.setEventPublisher(mockEventPublisher); aum.setThingRegistry(mockThingRegistry); - aum.addStateRevertListener(mockPredictionListener); } private void assertEvent(String expectedContent, String extectedSource) { @@ -105,19 +107,32 @@ private void assertEvent(String expectedContent, String extectedSource) { assertNothingHappened(); } + private void assertPredictionEvent(String expectedContent, String extectedSource) { + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(Event.class); + verify(mockEventPublisher).post(eventCaptor.capture()); + Event event = eventCaptor.getValue(); + assertTrue(event instanceof ItemStatePredictedEvent); + assertEquals(expectedContent, ((ItemStatePredictedEvent) event).getPredictedState().toFullString()); + assertEquals(extectedSource, event.getSource()); + assertNothingHappened(); + } + private void assertChangeStateTo() { - verify(mockPredictionListener).changeStateTo(isA(Item.class), eq(new StringType("AFTER"))); + assertPredictionEvent("AFTER", null); assertNothingHappened(); } private void assertKeepCurrentState() { - verify(mockPredictionListener).keepCurrentState(isA(Item.class)); + assertPredictionEvent("BEFORE", null); assertNothingHappened(); } private void assertNothingHappened() { verifyNoMoreInteractions(mockEventPublisher); - verifyNoMoreInteractions(mockPredictionListener); + } + + private void setAutoUpdatePolicy(ChannelUID channelUID, AutoUpdatePolicy policy) { + policies.put(channelUID, policy); } @Test @@ -178,7 +193,7 @@ public void testAutoUpdate_noPolicy_noChannel() { public void testAutoUpdate_policyVETO_thingONLINE() { links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.VETO); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.VETO); aum.receiveCommand(event, item); @@ -189,7 +204,7 @@ public void testAutoUpdate_policyVETO_thingONLINE() { public void testAutoUpdate_policyRECOMMEND() { links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.RECOMMEND); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.RECOMMEND); aum.receiveCommand(event, item); @@ -201,8 +216,8 @@ public void testAutoUpdate_policyVETObeatsDEFAULT() { links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_2)); when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.VETO); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_2, AutoUpdatePolicy.DEFAULT); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.VETO); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_2, AutoUpdatePolicy.DEFAULT); aum.receiveCommand(event, item); @@ -214,8 +229,8 @@ public void testAutoUpdate_policyVETObeatsRECOMMEND() { links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_2)); when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.VETO); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_2, AutoUpdatePolicy.RECOMMEND); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.VETO); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_2, AutoUpdatePolicy.RECOMMEND); aum.receiveCommand(event, item); @@ -227,8 +242,8 @@ public void testAutoUpdate_policyDEFAULTbeatsRECOMMEND() { links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_2)); when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.DEFAULT); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_2, AutoUpdatePolicy.RECOMMEND); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.DEFAULT); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_2, AutoUpdatePolicy.RECOMMEND); aum.receiveCommand(event, item); @@ -240,8 +255,8 @@ public void testAutoUpdate_errorInvalidatesVETO() { links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.RECOMMEND); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.VETO); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.RECOMMEND); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.VETO); aum.receiveCommand(event, item); @@ -253,8 +268,8 @@ public void testAutoUpdate_errorInvalidatesVETO2() { links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.DEFAULT); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.VETO); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.DEFAULT); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.VETO); aum.receiveCommand(event, item); @@ -266,8 +281,8 @@ public void testAutoUpdate_errorInvalidatesDEFAULT() { links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_1)); links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.RECOMMEND); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.DEFAULT); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_1, AutoUpdatePolicy.RECOMMEND); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.DEFAULT); aum.receiveCommand(event, item); @@ -279,7 +294,7 @@ public void testAutoUpdate_multipleErrors() { links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); links.add(new ItemChannelLink("test", CHANNEL_UID_ONLINE_GONE)); when(mockThingOnline.getStatus()).thenReturn(ThingStatus.ONLINE); - aum.setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.DEFAULT); + setAutoUpdatePolicy(CHANNEL_UID_ONLINE_GONE, AutoUpdatePolicy.DEFAULT); aum.receiveCommand(event, item); @@ -303,8 +318,8 @@ public void testAutoUpdate_sendOptimisticUpdates() { aum.receiveCommand(event, item); - verify(mockPredictionListener).changeStateTo(isA(Item.class), eq(new StringType("AFTER"))); - assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE_OPTIMISTIC); + assertPredictionEvent("AFTER", AutoUpdateManager.EVENT_SOURCE_OPTIMISTIC); + assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE_OPTIMISTIC); // no? } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Channel.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Channel.java index a4db00164ed..e1746af31f9 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Channel.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/Channel.java @@ -24,6 +24,7 @@ import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.items.Item; import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; +import org.eclipse.smarthome.core.thing.type.AutoUpdatePolicy; import org.eclipse.smarthome.core.thing.type.ChannelKind; import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; @@ -61,6 +62,8 @@ public class Channel { private Set defaultTags = new LinkedHashSet<>(); + private final AutoUpdatePolicy autoUpdatePolicy; + /** * Package protected default constructor to allow reflective instantiation. */ @@ -68,6 +71,7 @@ public class Channel { this.kind = ChannelKind.STATE; this.configuration = new Configuration(); this.properties = Collections.unmodifiableMap(new HashMap(0)); + this.autoUpdatePolicy = AutoUpdatePolicy.DEFAULT; } /** @@ -80,6 +84,7 @@ public Channel(ChannelUID uid, String acceptedItemType) { this.kind = ChannelKind.STATE; this.configuration = new Configuration(); this.properties = Collections.unmodifiableMap(new HashMap(0)); + this.autoUpdatePolicy = AutoUpdatePolicy.DEFAULT; } /** @@ -87,7 +92,8 @@ public Channel(ChannelUID uid, String acceptedItemType) { */ @Deprecated public Channel(ChannelUID uid, String acceptedItemType, Configuration configuration) { - this(uid, null, acceptedItemType, ChannelKind.STATE, configuration, new HashSet(0), null, null, null); + this(uid, null, acceptedItemType, ChannelKind.STATE, configuration, new HashSet(0), null, null, null, + AutoUpdatePolicy.DEFAULT); } /** @@ -95,7 +101,8 @@ public Channel(ChannelUID uid, String acceptedItemType, Configuration configurat */ @Deprecated public Channel(ChannelUID uid, String acceptedItemType, Set defaultTags) { - this(uid, null, acceptedItemType, ChannelKind.STATE, null, defaultTags, null, null, null); + this(uid, null, acceptedItemType, ChannelKind.STATE, null, defaultTags, null, null, null, + AutoUpdatePolicy.DEFAULT); } /** @@ -104,7 +111,8 @@ public Channel(ChannelUID uid, String acceptedItemType, Set defaultTags) @Deprecated public Channel(ChannelUID uid, String acceptedItemType, Configuration configuration, Set defaultTags, Map properties) { - this(uid, null, acceptedItemType, ChannelKind.STATE, null, defaultTags, properties, null, null); + this(uid, null, acceptedItemType, ChannelKind.STATE, null, defaultTags, properties, null, null, + AutoUpdatePolicy.DEFAULT); } /** @@ -113,13 +121,15 @@ public Channel(ChannelUID uid, String acceptedItemType, Configuration configurat @Deprecated public Channel(ChannelUID uid, @Nullable ChannelTypeUID channelTypeUID, @Nullable String acceptedItemType, ChannelKind kind, @Nullable Configuration configuration, Set defaultTags, - @Nullable Map properties, @Nullable String label, @Nullable String description) { + @Nullable Map properties, @Nullable String label, @Nullable String description, + AutoUpdatePolicy autoUpdatePolicy) { this.uid = uid; this.channelTypeUID = channelTypeUID; this.acceptedItemType = acceptedItemType; this.kind = kind; this.label = label; this.description = description; + this.autoUpdatePolicy = autoUpdatePolicy; this.defaultTags = Collections. unmodifiableSet(new HashSet(defaultTags)); if (configuration == null) { this.configuration = new Configuration(); @@ -219,4 +229,8 @@ public Set getDefaultTags() { return defaultTags; } + public AutoUpdatePolicy getAutoUpdatePolicy() { + return autoUpdatePolicy; + } + } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java index 51e81877cd2..c314b3cb11f 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/ThingHandlerCallback.java @@ -135,12 +135,4 @@ public interface ThingHandlerCallback { */ boolean isChannelLinked(ChannelUID channelUID); - /** - * Give advice to the framework whether automatic state updates should be sent for the given channel or not. - * - * @param channelUID - * @param policy - */ - void setAutoUpdatePolicy(ChannelUID channelUID, AutoUpdatePolicy policy); - } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java index f022f0178d8..1db43448554 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java @@ -21,6 +21,7 @@ import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Channel; import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.AutoUpdatePolicy; import org.eclipse.smarthome.core.thing.type.ChannelKind; import org.eclipse.smarthome.core.thing.type.ChannelType; import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; @@ -44,6 +45,7 @@ public class ChannelBuilder { private @Nullable String label; private @Nullable String description; private @Nullable ChannelTypeUID channelTypeUID; + private AutoUpdatePolicy autoUpdatePolicy = AutoUpdatePolicy.DEFAULT; private ChannelBuilder(ChannelUID channelUID, @Nullable String acceptedItemType, Set defaultTags) { this.channelUID = channelUID; @@ -166,6 +168,21 @@ public ChannelBuilder withKind(ChannelKind kind) { return this; } + /** + * Sets the auto update policy. See {@link AutoUpdatePolicy} for details. + * + * @param policy the auto update policy to use + * @return channel builder + */ + public ChannelBuilder withAutoUpdatePolicy(AutoUpdatePolicy policy) { + if (policy != null) { + this.autoUpdatePolicy = policy; + } else { + this.autoUpdatePolicy = AutoUpdatePolicy.DEFAULT; + } + return this; + } + /** * Builds and returns the channel. * @@ -173,7 +190,7 @@ public ChannelBuilder withKind(ChannelKind kind) { */ public Channel build() { return new Channel(channelUID, channelTypeUID, acceptedItemType, kind, configuration, defaultTags, properties, - label, description); + label, description, autoUpdatePolicy); } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java index 2e0c1f13ccc..6171658d607 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java @@ -16,7 +16,6 @@ import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -28,8 +27,8 @@ import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingRegistry; import org.eclipse.smarthome.core.thing.ThingStatus; -import org.eclipse.smarthome.core.thing.binding.AutoUpdatePolicy; import org.eclipse.smarthome.core.thing.link.ItemChannelLinkRegistry; +import org.eclipse.smarthome.core.thing.type.AutoUpdatePolicy; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; import org.osgi.service.component.annotations.Activate; @@ -63,8 +62,6 @@ public class AutoUpdateManager { private @NonNullByDefault({}) ThingRegistry thingRegistry; private @NonNullByDefault({}) EventPublisher eventPublisher; - private final Map autoUpdatePolicies = new ConcurrentHashMap<>(); - private boolean enabled = true; private boolean sendOptimisticUpdates = false; @@ -178,7 +175,7 @@ private Recommendation shouldAutoUpdate(String itemName) { continue; } - AutoUpdatePolicy policy = autoUpdatePolicies.get(channelUID); + AutoUpdatePolicy policy = thing.getChannel(channelUID.getId()).getAutoUpdatePolicy(); if (policy == null) { policy = AutoUpdatePolicy.DEFAULT; } @@ -244,10 +241,6 @@ private boolean isAcceptedState(State newState, Item item) { return isAccepted; } - protected void setAutoUpdatePolicy(ChannelUID channelUID, AutoUpdatePolicy policy) { - autoUpdatePolicies.put(channelUID, policy); - } - @Reference protected void setItemChannelLinkRegistry(ItemChannelLinkRegistry itemChannelLinkRegistry) { this.itemChannelLinkRegistry = itemChannelLinkRegistry; diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java index 3514d0e684d..d7c1a2e39e7 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java @@ -34,7 +34,6 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.config.core.ConfigDescription; import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; import org.eclipse.smarthome.config.core.ConfigDescriptionRegistry; @@ -61,7 +60,6 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.UID; -import org.eclipse.smarthome.core.thing.binding.AutoUpdatePolicy; import org.eclipse.smarthome.core.thing.binding.BridgeHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerCallback; @@ -303,11 +301,6 @@ public boolean isChannelLinked(ChannelUID channelUID) { return !itemChannelLinkRegistry.getLinks(channelUID).isEmpty(); } - @Override - public void setAutoUpdatePolicy(@NonNull ChannelUID channelUID, @NonNull AutoUpdatePolicy policy) { - autoUpdateManager.setAutoUpdatePolicy(channelUID, policy); - } - }; private ThingRegistryImpl thingRegistry; diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/AutoUpdatePolicy.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/AutoUpdatePolicy.java similarity index 96% rename from bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/AutoUpdatePolicy.java rename to bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/AutoUpdatePolicy.java index ca82db9c625..decbdaaaadb 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/AutoUpdatePolicy.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/AutoUpdatePolicy.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.eclipse.smarthome.core.thing.binding; +package org.eclipse.smarthome.core.thing.type; /** * A binding's recommendation to the framework whether a state update should be automatically sent to an item if a From 839ea72cdd05762372883a2a6061f59d4961c78a Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Fri, 22 Jun 2018 15:32:20 +0200 Subject: [PATCH 14/22] channel type having an auto update policy, thing factory using it Signed-off-by: Simon Kaufmann --- .../thing/internal/ThingFactoryHelper.java | 3 +- .../core/thing/type/ChannelType.java | 39 +++++++++++++++++++ .../thing/internal/GenericThingProvider.xtend | 6 ++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingFactoryHelper.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingFactoryHelper.java index 0514cec4496..1d2a5917157 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingFactoryHelper.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingFactoryHelper.java @@ -172,7 +172,8 @@ static ChannelBuilder createChannelBuilder(ChannelUID channelUID, ChannelType ch .withType(channelType.getUID()) // .withDefaultTags(channelType.getTags()) // .withKind(channelType.getKind()) // - .withLabel(channelType.getLabel()); + .withLabel(channelType.getLabel()) // + .withAutoUpdatePolicy(channelType.getAutoUpdatePolicy()); String description = channelType.getDescription(); if (description != null) { diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelType.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelType.java index 427af0d5e5d..64d88ac23e7 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelType.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelType.java @@ -42,6 +42,7 @@ public class ChannelType extends AbstractDescriptionType { private final StateDescription state; private final EventDescription event; private final URI configDescriptionURI; + private final AutoUpdatePolicy autoUpdatePolicy; /** * @deprecated Use the {@link ChannelTypeBuilder} instead. @@ -60,6 +61,34 @@ public ChannelType(ChannelTypeUID uid, boolean advanced, String itemType, String public ChannelType(ChannelTypeUID uid, boolean advanced, String itemType, ChannelKind kind, String label, String description, String category, Set tags, StateDescription state, EventDescription event, URI configDescriptionURI) throws IllegalArgumentException { + this(uid, advanced, itemType, kind, label, description, category, tags, state, event, configDescriptionURI, + AutoUpdatePolicy.DEFAULT); + } + + /** + * Creates a new instance of this class with the specified parameters. + * + * @param uid the unique identifier which identifies this Channel type within + * the overall system (must neither be null, nor empty) + * @param advanced true if this channel type contains advanced features, otherwise false + * @param itemType the item type of this Channel type, e.g. {@code ColorItem} + * @param kind the channel kind. + * @param label the human readable label for the according type + * (must neither be null nor empty) + * @param description the human readable description for the according type + * (could be null or empty) + * @param category the category of this Channel type, e.g. {@code TEMPERATURE} (could be null or empty) + * @param tags all tags of this {@link ChannelType}, e.g. {@code Alarm} (could be null or empty) + * @param state the restrictions of an item state which gives information how to interpret it + * (could be null) + * @param configDescriptionURI the link to the concrete ConfigDescription (could be null) + * @param autoUpdatePolicy the {@link AutoUpdatePolicy} to use. + * @throws IllegalArgumentException if the UID or the item type is null or empty, + * or the the meta information is null + */ + public ChannelType(ChannelTypeUID uid, boolean advanced, String itemType, ChannelKind kind, String label, + String description, String category, Set tags, StateDescription state, EventDescription event, + URI configDescriptionURI, AutoUpdatePolicy autoUpdatePolicy) throws IllegalArgumentException { super(uid, label, description); if (kind == null) { @@ -87,6 +116,7 @@ public ChannelType(ChannelTypeUID uid, boolean advanced, String itemType, Channe this.category = category; this.state = state; this.event = event; + this.autoUpdatePolicy = autoUpdatePolicy; } @Override @@ -176,4 +206,13 @@ public String getCategory() { return category; } + /** + * Returns the {@link AutoUpdatePolicy} of for channels of this type. + * + * @return the {@link AutoUpdatePolicy} + */ + public AutoUpdatePolicy getAutoUpdatePolicy() { + return autoUpdatePolicy; + } + } diff --git a/bundles/model/org.eclipse.smarthome.model.thing/src/org/eclipse/smarthome/model/thing/internal/GenericThingProvider.xtend b/bundles/model/org.eclipse.smarthome.model.thing/src/org/eclipse/smarthome/model/thing/internal/GenericThingProvider.xtend index 15105e2d61a..c9b3e848b0c 100644 --- a/bundles/model/org.eclipse.smarthome.model.thing/src/org/eclipse/smarthome/model/thing/internal/GenericThingProvider.xtend +++ b/bundles/model/org.eclipse.smarthome.model.thing/src/org/eclipse/smarthome/model/thing/internal/GenericThingProvider.xtend @@ -61,6 +61,7 @@ import org.osgi.service.component.annotations.Component import org.osgi.service.component.annotations.Reference import org.slf4j.Logger import org.slf4j.LoggerFactory +import org.eclipse.smarthome.core.thing.type.AutoUpdatePolicy /** * {@link ThingProvider} implementation which computes *.things files. @@ -349,6 +350,7 @@ class GenericThingProvider extends AbstractProvider implements ThingProvi var String itemType var label = it.label val configuration = createConfiguration + var autoUpdatePolicy = AutoUpdatePolicy.DEFAULT if (it.channelType !== null) { channelTypeUID = new ChannelTypeUID(thingUID.bindingId, it.channelType) val resolvedChannelType = channelTypeUID.channelType @@ -358,6 +360,7 @@ class GenericThingProvider extends AbstractProvider implements ThingProvi if (label === null) { label = resolvedChannelType.label } + autoUpdatePolicy = resolvedChannelType.autoUpdatePolicy applyDefaultConfiguration(configuration, resolvedChannelType) } else { logger.error("Channel type {} could not be resolved.", channelTypeUID.asString) @@ -374,6 +377,7 @@ class GenericThingProvider extends AbstractProvider implements ThingProvi .withConfiguration(configuration) .withType(channelTypeUID) .withLabel(label) + .withAutoUpdatePolicy(autoUpdatePolicy) channels += channel.build() } ] @@ -383,7 +387,7 @@ class GenericThingProvider extends AbstractProvider implements ThingProvi if (channelType !== null) { channels += ChannelBuilder.create(new ChannelUID(thingTypeUID, thingUID, id), channelType.itemType). - withType(it.channelTypeUID).build + withType(it.channelTypeUID).withAutoUpdatePolicy(channelType.autoUpdatePolicy).build } else { logger.warn( "Could not create channel '{}' for thing '{}', because channel type '{}' could not be found.", From 4c45b7da48dd37de36277e8b0836d0484d092f83 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Fri, 22 Jun 2018 16:26:36 +0200 Subject: [PATCH 15/22] support in XMLs for autoUpdatePolicy Signed-off-by: Simon Kaufmann --- .../thing/xml/internal/ChannelConverter.java | 22 ++++++++++--- .../xml/internal/ChannelTypeConverter.java | 14 +++++++- .../thing/xml/internal/ChannelXmlResult.java | 21 +++++++++--- .../thing-description-1.0.0.xsd | 10 ++++++ .../thing/binding/builder/ChannelBuilder.java | 2 +- .../type/StateChannelTypeBuilderImpl.java | 12 +++++-- .../core/thing/type/ChannelDefinition.java | 33 +++++++++++++++++-- .../thing/type/StateChannelTypeBuilder.java | 8 +++++ .../thing/internal/GenericThingProvider.xtend | 2 +- .../development/bindings/thing-definition.md | 32 ++++++++++++++++++ .../development/bindings/xml-reference.md | 1 + 11 files changed, 140 insertions(+), 17 deletions(-) diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelConverter.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelConverter.java index 5c0118108d5..0182e8e7f4d 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelConverter.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelConverter.java @@ -13,12 +13,14 @@ package org.eclipse.smarthome.core.thing.xml.internal; import java.util.List; +import java.util.Locale; import java.util.Map; import org.eclipse.smarthome.config.xml.util.ConverterAttributeMapValidator; import org.eclipse.smarthome.config.xml.util.GenericUnmarshaller; import org.eclipse.smarthome.config.xml.util.NodeIterator; import org.eclipse.smarthome.config.xml.util.NodeValue; +import org.eclipse.smarthome.core.thing.type.AutoUpdatePolicy; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.Converter; @@ -31,20 +33,20 @@ * into a {@link ChannelXmlResult} object. *

* This converter converts {@code channel} XML tags. - * + * * @author Chris Jackson - Initial Contribution * @author Simon Kaufmann - Fixing wrong inheritance * @author Chris Jackson - Added label and description */ public class ChannelConverter extends GenericUnmarshaller { - private ConverterAttributeMapValidator attributeMapValidator; + private final ConverterAttributeMapValidator attributeMapValidator; public ChannelConverter() { super(ChannelXmlResult.class); - attributeMapValidator = new ConverterAttributeMapValidator(new String[][] { { "id", "true" }, - { "typeId", "false" } }); + attributeMapValidator = new ConverterAttributeMapValidator( + new String[][] { { "id", "true" }, { "typeId", "false" } }); } @SuppressWarnings("unchecked") @@ -58,13 +60,23 @@ protected ChannelXmlResult unmarshalType(HierarchicalStreamReader reader, Unmars String typeId = attributes.get("typeId"); String label = (String) nodeIterator.nextValue("label", false); String description = (String) nodeIterator.nextValue("description", false); + AutoUpdatePolicy autoUpdatePolicy = readAutoUpdatePlicy(nodeIterator); List properties = getProperties(nodeIterator); - ChannelXmlResult channelXmlResult = new ChannelXmlResult(id, typeId, label, description, properties); + ChannelXmlResult channelXmlResult = new ChannelXmlResult(id, typeId, label, description, properties, + autoUpdatePolicy); return channelXmlResult; } + private AutoUpdatePolicy readAutoUpdatePlicy(NodeIterator nodeIterator) { + String string = (String) nodeIterator.nextValue("autoUpdatePolicy", false); + if (string != null) { + return AutoUpdatePolicy.valueOf(string.toUpperCase(Locale.ENGLISH)); + } + return null; + } + @Override public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { // read attributes diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelTypeConverter.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelTypeConverter.java index 6bb3247caf8..6ea9a610804 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelTypeConverter.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelTypeConverter.java @@ -15,6 +15,7 @@ import java.net.URI; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -22,6 +23,7 @@ import org.eclipse.smarthome.config.xml.util.ConverterAttributeMapValidator; import org.eclipse.smarthome.config.xml.util.NodeIterator; import org.eclipse.smarthome.config.xml.util.NodeValue; +import org.eclipse.smarthome.core.thing.type.AutoUpdatePolicy; import org.eclipse.smarthome.core.thing.type.ChannelKind; import org.eclipse.smarthome.core.thing.type.ChannelType; import org.eclipse.smarthome.core.thing.type.ChannelTypeBuilder; @@ -76,6 +78,14 @@ private String readCategory(NodeIterator nodeIterator) throws ConversionExceptio return (String) nodeIterator.nextValue("category", false); } + private AutoUpdatePolicy readAutoUpdatePolicy(NodeIterator nodeIterator) { + String string = (String) nodeIterator.nextValue("autoUpdatePolicy", false); + if (string != null) { + return AutoUpdatePolicy.valueOf(string.toUpperCase(Locale.ENGLISH)); + } + return null; + } + private Set readTags(NodeIterator nodeIterator) throws ConversionException { Set tags = null; @@ -145,6 +155,7 @@ protected ChannelTypeXmlResult unmarshalType(HierarchicalStreamReader reader, Un String description = super.readDescription(nodeIterator); String category = readCategory(nodeIterator); Set tags = readTags(nodeIterator); + AutoUpdatePolicy autoUpdatePolicy = readAutoUpdatePolicy(nodeIterator); StateDescription stateDescription = readStateDescription(nodeIterator); EventDescription eventDescription = readEventDescription(nodeIterator); @@ -162,7 +173,8 @@ protected ChannelTypeXmlResult unmarshalType(HierarchicalStreamReader reader, Un if (cKind == ChannelKind.STATE) { channelType = ChannelTypeBuilder.state(channelTypeUID, label, itemType).isAdvanced(advanced) .withDescription(description).withCategory(category).withTags(tags) - .withConfigDescriptionURI(configDescriptionURI).withStateDescription(stateDescription).build(); + .withConfigDescriptionURI(configDescriptionURI).withStateDescription(stateDescription) + .withAutoUpdatePolicy(autoUpdatePolicy).build(); } else if (cKind == ChannelKind.TRIGGER) { channelType = ChannelTypeBuilder.trigger(channelTypeUID, label).isAdvanced(advanced) .withDescription(description).withCategory(category).withTags(tags) diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelXmlResult.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelXmlResult.java index f5899d10099..652e215596d 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelXmlResult.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelXmlResult.java @@ -18,6 +18,7 @@ import java.util.Map; import org.eclipse.smarthome.config.xml.util.NodeValue; +import org.eclipse.smarthome.core.thing.type.AutoUpdatePolicy; import org.eclipse.smarthome.core.thing.type.ChannelDefinition; import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; @@ -30,11 +31,12 @@ */ public class ChannelXmlResult { - private String id; - private String typeId; + private final String id; + private final String typeId; String label; String description; List properties; + private final AutoUpdatePolicy autoUpdatePolicy; /** * Constructs a new {@link ChannelXmlResult} @@ -45,12 +47,14 @@ public class ChannelXmlResult { * @param description the channel description * @param properties a {@link List} of channel properties */ - public ChannelXmlResult(String id, String typeId, String label, String description, List properties) { + public ChannelXmlResult(String id, String typeId, String label, String description, List properties, + AutoUpdatePolicy autoUpdatePolicy) { this.id = id; this.typeId = typeId; this.label = label; this.description = description; this.properties = properties; + this.autoUpdatePolicy = autoUpdatePolicy; } /** @@ -101,6 +105,15 @@ public String getDescription() { return description; } + /** + * Get the auto update policy for this channel. + * + * @return the auto update policy + */ + public AutoUpdatePolicy getAutoUpdatePolicy() { + return autoUpdatePolicy; + } + @Override public String toString() { return "ChannelTypeXmlResult [id=" + id + ", typeId=" + typeId + ", properties=" + properties + "]"; @@ -119,7 +132,7 @@ protected ChannelDefinition toChannelDefinition(String bindingId) throws Convers } ChannelDefinition channelDefinition = new ChannelDefinition(id, new ChannelTypeUID(typeUID), propertiesMap, - getLabel(), getDescription()); + getLabel(), getDescription(), getAutoUpdatePolicy()); return channelDefinition; } diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/thing-description-1.0.0.xsd b/bundles/core/org.eclipse.smarthome.core.thing.xml/thing-description-1.0.0.xsd index 58993995127..8ce02fd4b01 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/thing-description-1.0.0.xsd +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/thing-description-1.0.0.xsd @@ -53,6 +53,14 @@ + + + + + + + + @@ -63,6 +71,7 @@ + @@ -111,6 +120,7 @@ + diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java index 1db43448554..cfa3238b8a3 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/binding/builder/ChannelBuilder.java @@ -174,7 +174,7 @@ public ChannelBuilder withKind(ChannelKind kind) { * @param policy the auto update policy to use * @return channel builder */ - public ChannelBuilder withAutoUpdatePolicy(AutoUpdatePolicy policy) { + public ChannelBuilder withAutoUpdatePolicy(@Nullable AutoUpdatePolicy policy) { if (policy != null) { this.autoUpdatePolicy = policy; } else { diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/type/StateChannelTypeBuilderImpl.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/type/StateChannelTypeBuilderImpl.java index 75dc7e70900..d57b6809c68 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/type/StateChannelTypeBuilderImpl.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/type/StateChannelTypeBuilderImpl.java @@ -15,6 +15,7 @@ import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.type.AutoUpdatePolicy; import org.eclipse.smarthome.core.thing.type.ChannelKind; import org.eclipse.smarthome.core.thing.type.ChannelType; import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; @@ -31,8 +32,9 @@ public class StateChannelTypeBuilderImpl extends AbstractChannelTypeBuilder implements StateChannelTypeBuilder { - private @Nullable StateDescription stateDescription; private final String itemType; + private @Nullable StateDescription stateDescription; + private @Nullable AutoUpdatePolicy autoUpdatePolicy; public StateChannelTypeBuilderImpl(ChannelTypeUID channelTypeUID, String label, String itemType) { super(channelTypeUID, label); @@ -50,10 +52,16 @@ public StateChannelTypeBuilder withStateDescription(@Nullable StateDescription s return this; } + @Override + public StateChannelTypeBuilder withAutoUpdatePolicy(@Nullable AutoUpdatePolicy autoUpdatePolicy) { + this.autoUpdatePolicy = autoUpdatePolicy; + return this; + } + @Override public ChannelType build() { return new ChannelType(channelTypeUID, advanced, itemType, ChannelKind.STATE, label, description, category, - tags.isEmpty() ? null : tags, stateDescription, null, configDescriptionURI); + tags.isEmpty() ? null : tags, stateDescription, null, configDescriptionURI, autoUpdatePolicy); } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelDefinition.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelDefinition.java index 2ee55890202..e3a693937b7 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelDefinition.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/ChannelDefinition.java @@ -32,11 +32,12 @@ */ public class ChannelDefinition { - private String id; - private ChannelTypeUID channelTypeUID; + private final String id; + private final ChannelTypeUID channelTypeUID; private final Map properties; private final String label; private final String description; + private final AutoUpdatePolicy autoUpdatePolicy; /** * Creates a new instance of this class with the specified parameters. @@ -46,7 +47,7 @@ public class ChannelDefinition { * @throws IllegalArgumentException if the ID is null or empty, or the type is null */ public ChannelDefinition(String id, ChannelTypeUID channelTypeUID) throws IllegalArgumentException { - this(id, channelTypeUID, null, null, null); + this(id, channelTypeUID, null, null, null, null); } /** @@ -61,6 +62,22 @@ public ChannelDefinition(String id, ChannelTypeUID channelTypeUID) throws Illega */ public ChannelDefinition(String id, ChannelTypeUID channelTypeUID, Map properties, String label, String description) throws IllegalArgumentException { + this(id, channelTypeUID, properties, label, description, null); + } + + /** + * Creates a new instance of this class with the specified parameters. + * + * @param id the identifier of the channel (must neither be null nor empty) + * @param channelTypeUID the type UID of the channel (must not be null) + * @param properties the properties this Channel provides (could be null) + * @param label the label for the channel to override channelType (could be null) + * @param description the description for the channel to override channelType (could be null) + * @param autoUpdatePolicy the auto update policy for the channel to override from the thing type (could be null) + * @throws IllegalArgumentException if the ID is null or empty, or the type is null + */ + public ChannelDefinition(String id, ChannelTypeUID channelTypeUID, Map properties, String label, + String description, AutoUpdatePolicy autoUpdatePolicy) throws IllegalArgumentException { if ((id == null) || (id.isEmpty())) { throw new IllegalArgumentException("The ID must neither be null nor empty!"); } @@ -79,6 +96,7 @@ public ChannelDefinition(String id, ChannelTypeUID channelTypeUID, Map getProperties() { return properties; } + /** + * Returns the {@link AutoUpdatePolicy} to use for this channel. + * + * @return the auto update policy + */ + public AutoUpdatePolicy getAutoUpdatePolicy() { + return autoUpdatePolicy; + } + @Override public String toString() { return "ChannelDefinition [id=" + id + ", type=" + channelTypeUID + ", properties=" + properties + "]"; diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/StateChannelTypeBuilder.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/StateChannelTypeBuilder.java index 5e25fcd2ca4..78188b71655 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/StateChannelTypeBuilder.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/StateChannelTypeBuilder.java @@ -33,4 +33,12 @@ public interface StateChannelTypeBuilder extends ChannelTypeBuilder implements ThingProvi var String itemType var label = it.label val configuration = createConfiguration - var autoUpdatePolicy = AutoUpdatePolicy.DEFAULT + var AutoUpdatePolicy autoUpdatePolicy = null; if (it.channelType !== null) { channelTypeUID = new ChannelTypeUID(thingUID.bindingId, it.channelType) val resolvedChannelType = channelTypeUID.channelType diff --git a/docs/documentation/development/bindings/thing-definition.md b/docs/documentation/development/bindings/thing-definition.md index 71dbec44d7f..4214f644e84 100644 --- a/docs/documentation/development/bindings/thing-definition.md +++ b/docs/documentation/development/bindings/thing-definition.md @@ -365,3 +365,35 @@ The description can include limited HTML to enhance the display of this informat The following HTML tags are allowed : ```,
, ,

,

,

,

,

,
, ,

, , , , ,

    ,
      ,
    1. ```. These must be inside the XML escape sequence - e.g. ``````. + +## Auto Update Policies + +Channel types can optionally define a policy with respect to the auto update handling. +This influences the decision within the framework if an auto-update of the item's state should be sent in case a command is received for it. +The auto update policy typically is inherited by the channel from its channel type. +Nevertheless, this value can be overridden in the channel definition. + +In this example, an auto update policy is defined for the channel type, but is overridden in the channel definition: + +```xml + + + recommend + + + + + Thing type which overrides the auto update policy of a channel + + + default + + + +``` + +The following policies are supported: + +* **veto**: No automatic state update should be sent by the framework. The thing handler will make sure it sends a state update and it can to it better than just converting the command to a state. +* **default**: The binding does not care and the framework may do what it deems to be right. The state update which the framework will send out normally will correspond the command state anyway. This is the default of no other policy is set explicitly. +* **recommend**: An automatic state update should be sent by the framework because no updates will be sent by the binding. This usually is the case when devices don't expose their current state to the handler. diff --git a/docs/documentation/development/bindings/xml-reference.md b/docs/documentation/development/bindings/xml-reference.md index 539b450500b..cd94697504b 100644 --- a/docs/documentation/development/bindings/xml-reference.md +++ b/docs/documentation/development/bindings/xml-reference.md @@ -487,6 +487,7 @@ Bridge and *Thing* descriptions must be placed as XML file(s) (with the ending ` optionThe description for the option (optional). option.valueThe value for the option (mandatory). Note that the value may be outside of the range specified in the min/max if this is specified. eventThe restrictions of an trigger event which gives information how to interpret it (optional). + autoUpdatePolicyThe auto update policy to use (optional). config-descriptionThe configuration description for the channel within the ConfigDescriptionRegistry (optional). config-description-refThe reference to a configuration description for the channel within the ConfigDescriptionRegistry (optional). config-description-ref.uriThe URI of the configuration description for the channel within the ConfigDescriptionRegistry (mandatory). From d697b99052bb173e14598fb848332af96edfb802 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Fri, 22 Jun 2018 17:02:29 +0200 Subject: [PATCH 16/22] consider the user's opinion via metadata Signed-off-by: Simon Kaufmann --- .../thing/internal/AutoUpdateManagerTest.java | 30 ++++----- .../META-INF/MANIFEST.MF | 1 + .../AutoUpdateConfigDescriptionProvider.java | 61 +++++++++++++++++++ .../thing/internal/AutoUpdateManager.java | 31 +++++++++- 4 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateConfigDescriptionProvider.java diff --git a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java index 045caaa9a30..6b53e3bc847 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.test/src/test/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManagerTest.java @@ -12,7 +12,7 @@ */ package org.eclipse.smarthome.core.thing.internal; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; @@ -26,6 +26,7 @@ import org.eclipse.smarthome.core.events.Event; import org.eclipse.smarthome.core.events.EventPublisher; import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.items.MetadataRegistry; import org.eclipse.smarthome.core.items.events.ItemCommandEvent; import org.eclipse.smarthome.core.items.events.ItemEventFactory; import org.eclipse.smarthome.core.items.events.ItemStateEvent; @@ -68,6 +69,7 @@ public class AutoUpdateManagerTest { private @Mock Thing mockThingOnline; private @Mock Thing mockThingHandlerMissing; private @Mock ThingHandler mockHandler; + private @Mock MetadataRegistry mockMetadataRegistry; private final List links = new LinkedList<>(); private AutoUpdateManager aum; @@ -95,13 +97,13 @@ public void setup() { aum.setItemChannelLinkRegistry(mockLinkRegistry); aum.setEventPublisher(mockEventPublisher); aum.setThingRegistry(mockThingRegistry); + aum.setMetadataRegistry(mockMetadataRegistry); } - private void assertEvent(String expectedContent, String extectedSource) { + private void assertStateEvent(String expectedContent, String extectedSource) { ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(Event.class); - verify(mockEventPublisher).post(eventCaptor.capture()); - Event event = eventCaptor.getValue(); - assertTrue(event instanceof ItemStateEvent); + verify(mockEventPublisher, atLeastOnce()).post(eventCaptor.capture()); + Event event = eventCaptor.getAllValues().stream().filter(e -> e instanceof ItemStateEvent).findFirst().get(); assertEquals(expectedContent, ((ItemStateEvent) event).getItemState().toFullString()); assertEquals(extectedSource, event.getSource()); assertNothingHappened(); @@ -109,9 +111,9 @@ private void assertEvent(String expectedContent, String extectedSource) { private void assertPredictionEvent(String expectedContent, String extectedSource) { ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(Event.class); - verify(mockEventPublisher).post(eventCaptor.capture()); - Event event = eventCaptor.getValue(); - assertTrue(event instanceof ItemStatePredictedEvent); + verify(mockEventPublisher, atLeastOnce()).post(eventCaptor.capture()); + Event event = eventCaptor.getAllValues().stream().filter(e -> e instanceof ItemStatePredictedEvent).findFirst() + .get(); assertEquals(expectedContent, ((ItemStatePredictedEvent) event).getPredictedState().toFullString()); assertEquals(extectedSource, event.getSource()); assertNothingHappened(); @@ -139,7 +141,7 @@ private void setAutoUpdatePolicy(ChannelUID channelUID, AutoUpdatePolicy policy) public void testAutoUpdate_noLink() { aum.receiveCommand(event, item); - assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); + assertStateEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); } @Test @@ -208,7 +210,7 @@ public void testAutoUpdate_policyRECOMMEND() { aum.receiveCommand(event, item); - assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); + assertStateEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); } @Test @@ -260,7 +262,7 @@ public void testAutoUpdate_errorInvalidatesVETO() { aum.receiveCommand(event, item); - assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); + assertStateEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); } @Test @@ -286,7 +288,7 @@ public void testAutoUpdate_errorInvalidatesDEFAULT() { aum.receiveCommand(event, item); - assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); + assertStateEvent("AFTER", AutoUpdateManager.EVENT_SOURCE); } @Test @@ -318,8 +320,8 @@ public void testAutoUpdate_sendOptimisticUpdates() { aum.receiveCommand(event, item); - assertPredictionEvent("AFTER", AutoUpdateManager.EVENT_SOURCE_OPTIMISTIC); - assertEvent("AFTER", AutoUpdateManager.EVENT_SOURCE_OPTIMISTIC); // no? + assertPredictionEvent("AFTER", null); + assertStateEvent("AFTER", AutoUpdateManager.EVENT_SOURCE_OPTIMISTIC); // no? } } diff --git a/bundles/core/org.eclipse.smarthome.core.thing/META-INF/MANIFEST.MF b/bundles/core/org.eclipse.smarthome.core.thing/META-INF/MANIFEST.MF index 0e43a50cfef..f10cc26af2f 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/META-INF/MANIFEST.MF +++ b/bundles/core/org.eclipse.smarthome.core.thing/META-INF/MANIFEST.MF @@ -33,6 +33,7 @@ Import-Package: org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.core.dto, + org.eclipse.smarthome.config.core.metadata, org.eclipse.smarthome.config.core.status, org.eclipse.smarthome.config.core.validation, org.eclipse.smarthome.core.common, diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateConfigDescriptionProvider.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateConfigDescriptionProvider.java new file mode 100644 index 00000000000..bb75b8b5771 --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateConfigDescriptionProvider.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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.eclipse.smarthome.core.thing.internal; + +import static java.util.stream.Collectors.toList; + +import java.util.List; +import java.util.Locale; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; +import org.eclipse.smarthome.config.core.ParameterOption; +import org.eclipse.smarthome.config.core.metadata.MetadataConfigDescriptionProvider; +import org.osgi.service.component.annotations.Component; + +/** + * Provider of the config description for the auto update policy metadata. + * + * @author Simon Kaufmann - initial contribution and API + * + */ +@NonNullByDefault +@Component +public class AutoUpdateConfigDescriptionProvider implements MetadataConfigDescriptionProvider { + + @Override + public String getNamespace() { + return "autoupdate"; + } + + @Override + public @Nullable String getDescription(@Nullable Locale locale) { + return "Auto Update"; + } + + @Override + public @Nullable List getParameterOptions(@Nullable Locale locale) { + return Stream.of( // + new ParameterOption("true", "Enforce an auto update"), // + new ParameterOption("false", "Veto an auto update") // + ).collect(toList()); + } + + @Override + public @Nullable List getParameters(String value, @Nullable Locale locale) { + return null; + } + +} diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java index 6171658d607..d38ed0efbeb 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java @@ -21,6 +21,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.events.EventPublisher; import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.Metadata; +import org.eclipse.smarthome.core.items.MetadataKey; +import org.eclipse.smarthome.core.items.MetadataRegistry; import org.eclipse.smarthome.core.items.events.ItemCommandEvent; import org.eclipse.smarthome.core.items.events.ItemEventFactory; import org.eclipse.smarthome.core.thing.ChannelUID; @@ -61,6 +64,7 @@ public class AutoUpdateManager { private @NonNullByDefault({}) ItemChannelLinkRegistry itemChannelLinkRegistry; private @NonNullByDefault({}) ThingRegistry thingRegistry; private @NonNullByDefault({}) EventPublisher eventPublisher; + private @NonNullByDefault({}) MetadataRegistry metadataRegistry; private boolean enabled = true; private boolean sendOptimisticUpdates = false; @@ -123,10 +127,22 @@ public void receiveCommand(ItemCommandEvent commandEvent, Item item) { if (command instanceof State) { final State state = (State) command; - // TODO: consider user-override via item meta-data - // (see https://github.com/eclipse/smarthome/pull/4390) Recommendation autoUpdate = shouldAutoUpdate(itemName); + // consider user-override via item meta-data + MetadataKey key = new MetadataKey("autoupdate", itemName); + Metadata metadata = metadataRegistry.get(key); + if (metadata != null && !metadata.getValue().trim().isEmpty()) { + boolean override = Boolean.getBoolean(metadata.getValue()); + if (override) { + logger.trace("Auto update strategy {} overriden by item metadata to REQUIRED", autoUpdate); + autoUpdate = Recommendation.REQUIRED; + } else { + logger.trace("Auto update strategy {} overriden by item metadata to DONT", autoUpdate); + autoUpdate = Recommendation.DONT; + } + } + switch (autoUpdate) { case REQUIRED: logger.trace("Automatically updating item '{}' because no channel is linked", itemName); @@ -179,6 +195,7 @@ private Recommendation shouldAutoUpdate(String itemName) { if (policy == null) { policy = AutoUpdatePolicy.DEFAULT; } + switch (policy) { case VETO: ret = Recommendation.DONT; @@ -195,6 +212,7 @@ private Recommendation shouldAutoUpdate(String itemName) { break; } } + return ret; } @@ -268,4 +286,13 @@ protected void unsetEventPublisher(EventPublisher eventPublisher) { this.eventPublisher = null; } + @Reference + protected void setMetadataRegistry(MetadataRegistry metadataRegistry) { + this.metadataRegistry = metadataRegistry; + } + + protected void unsetMetadataRegistry(MetadataRegistry metadataRegistry) { + this.metadataRegistry = null; + } + } From ddb8f82fdb17533c82a936a89bd81143c1788a06 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Tue, 3 Jul 2018 11:11:37 +0200 Subject: [PATCH 17/22] fixed typos Signed-off-by: Simon Kaufmann --- .../eclipse/smarthome/core/thing/type/AutoUpdatePolicy.java | 4 ++-- docs/documentation/development/bindings/thing-definition.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/AutoUpdatePolicy.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/AutoUpdatePolicy.java index decbdaaaadb..ef3af20beef 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/AutoUpdatePolicy.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/type/AutoUpdatePolicy.java @@ -22,13 +22,13 @@ public enum AutoUpdatePolicy { /** * No automatic state update should be sent by the framework. The handler will make sure it sends a state update and - * it can to it better than just converting the command to a state. + * it can do it better than just converting the command to a state. */ VETO, /** * The binding does not care and the framework may do what it deems to be right. The state update which the - * framework will send out normally will correspond the command state anyway. This is the default of no other policy + * framework will send out normally will correspond the command state anyway. This is the default if no other policy * is set. */ DEFAULT, diff --git a/docs/documentation/development/bindings/thing-definition.md b/docs/documentation/development/bindings/thing-definition.md index 4214f644e84..88b2507fb8d 100644 --- a/docs/documentation/development/bindings/thing-definition.md +++ b/docs/documentation/development/bindings/thing-definition.md @@ -394,6 +394,6 @@ In this example, an auto update policy is defined for the channel type, but is o The following policies are supported: -* **veto**: No automatic state update should be sent by the framework. The thing handler will make sure it sends a state update and it can to it better than just converting the command to a state. -* **default**: The binding does not care and the framework may do what it deems to be right. The state update which the framework will send out normally will correspond the command state anyway. This is the default of no other policy is set explicitly. +* **veto**: No automatic state update should be sent by the framework. The thing handler will make sure it sends a state update and it can do it better than just converting the command to a state. +* **default**: The binding does not care and the framework may do what it deems to be right. The state update which the framework will send out normally will correspond the command state anyway. This is the default if no other policy is set explicitly. * **recommend**: An automatic state update should be sent by the framework because no updates will be sent by the binding. This usually is the case when devices don't expose their current state to the handler. From 964d1ef72315996afc15fb8d94c453469260af51 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Tue, 10 Jul 2018 15:41:18 +0200 Subject: [PATCH 18/22] review feedback Signed-off-by: Simon Kaufmann --- .../core/thing/xml/internal/ChannelConverter.java | 4 ++-- .../smarthome/core/thing/internal/AutoUpdateManager.java | 7 ++++--- .../io/rest/sitemap/SitemapSubscriptionService.java | 2 ++ .../io/rest/sitemap/internal/PageChangeListener.java | 6 ++++-- .../documentation/development/bindings/thing-definition.md | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelConverter.java b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelConverter.java index 0182e8e7f4d..acd63e94796 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelConverter.java +++ b/bundles/core/org.eclipse.smarthome.core.thing.xml/src/main/java/org/eclipse/smarthome/core/thing/xml/internal/ChannelConverter.java @@ -60,7 +60,7 @@ protected ChannelXmlResult unmarshalType(HierarchicalStreamReader reader, Unmars String typeId = attributes.get("typeId"); String label = (String) nodeIterator.nextValue("label", false); String description = (String) nodeIterator.nextValue("description", false); - AutoUpdatePolicy autoUpdatePolicy = readAutoUpdatePlicy(nodeIterator); + AutoUpdatePolicy autoUpdatePolicy = readAutoUpdatePolicy(nodeIterator); List properties = getProperties(nodeIterator); ChannelXmlResult channelXmlResult = new ChannelXmlResult(id, typeId, label, description, properties, @@ -69,7 +69,7 @@ protected ChannelXmlResult unmarshalType(HierarchicalStreamReader reader, Unmars return channelXmlResult; } - private AutoUpdatePolicy readAutoUpdatePlicy(NodeIterator nodeIterator) { + private AutoUpdatePolicy readAutoUpdatePolicy(NodeIterator nodeIterator) { String string = (String) nodeIterator.nextValue("autoUpdatePolicy", false); if (string != null) { return AutoUpdatePolicy.valueOf(string.toUpperCase(Locale.ENGLISH)); diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java index d38ed0efbeb..67a8e0008ab 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/AutoUpdateManager.java @@ -50,9 +50,10 @@ */ @NonNullByDefault @Component(immediate = true, service = { - AutoUpdateManager.class }, configurationPid = "org.eclipse.smarthome.autoupdatemanager", configurationPolicy = ConfigurationPolicy.OPTIONAL) + AutoUpdateManager.class }, configurationPid = "org.eclipse.smarthome.autoupdate", configurationPolicy = ConfigurationPolicy.OPTIONAL) public class AutoUpdateManager { + private static final String AUTOUPDATE_KEY = "autoupdate"; protected static final String EVENT_SOURCE = "org.eclipse.smarthome.core.autoupdate"; protected static final String EVENT_SOURCE_OPTIMISTIC = "org.eclipse.smarthome.core.autoupdate.optimistic"; @@ -130,10 +131,10 @@ public void receiveCommand(ItemCommandEvent commandEvent, Item item) { Recommendation autoUpdate = shouldAutoUpdate(itemName); // consider user-override via item meta-data - MetadataKey key = new MetadataKey("autoupdate", itemName); + MetadataKey key = new MetadataKey(AUTOUPDATE_KEY, itemName); Metadata metadata = metadataRegistry.get(key); if (metadata != null && !metadata.getValue().trim().isEmpty()) { - boolean override = Boolean.getBoolean(metadata.getValue()); + boolean override = Boolean.parseBoolean(metadata.getValue()); if (override) { logger.trace("Auto update strategy {} overriden by item metadata to REQUIRED", autoUpdate); autoUpdate = Recommendation.REQUIRED; diff --git a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java index ac4c49acd91..11a1d6d8316 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java @@ -366,6 +366,8 @@ public void receive(Event event) { ItemStatePredictedEvent prediction = (ItemStatePredictedEvent) event; Item item = itemUIRegistry.get(prediction.getItemName()); if (item instanceof GroupItem) { + // don't send out auto-update events for group items as those will calculate their state based on their + // members and predictions aren't really possible in that case (or at least would be highly complex). return; } for (PageChangeListener pageChangeListener : pageChangeListeners.values()) { diff --git a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java index 492e6463d48..67280a18c30 100644 --- a/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java +++ b/bundles/io/org.eclipse.smarthome.io.rest.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java @@ -47,7 +47,9 @@ */ public class PageChangeListener implements StateChangeListener { - private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("ui"); + private static final int REVERT_INTERVAL = 300; + private final ScheduledExecutorService scheduler = ThreadPoolManager + .getScheduledPool(ThreadPoolManager.THREAD_POOL_NAME_COMMON); private final String sitemapName; private final String pageId; private final ItemUIRegistry itemUIRegistry; @@ -200,7 +202,7 @@ public void stateUpdated(Item item, State state) { public void keepCurrentState(Item item) { scheduler.schedule(() -> { constructAndSendEvents(item, item.getState()); - }, 200, TimeUnit.MILLISECONDS); + }, REVERT_INTERVAL, TimeUnit.MILLISECONDS); } public void changeStateTo(Item item, State state) { diff --git a/docs/documentation/development/bindings/thing-definition.md b/docs/documentation/development/bindings/thing-definition.md index 88b2507fb8d..61e19ccaf95 100644 --- a/docs/documentation/development/bindings/thing-definition.md +++ b/docs/documentation/development/bindings/thing-definition.md @@ -396,4 +396,4 @@ The following policies are supported: * **veto**: No automatic state update should be sent by the framework. The thing handler will make sure it sends a state update and it can do it better than just converting the command to a state. * **default**: The binding does not care and the framework may do what it deems to be right. The state update which the framework will send out normally will correspond the command state anyway. This is the default if no other policy is set explicitly. -* **recommend**: An automatic state update should be sent by the framework because no updates will be sent by the binding. This usually is the case when devices don't expose their current state to the handler. +* **recommend**: An automatic state update should be sent by the framework because no updates are sent by the binding. This usually is the case when devices don't expose their current state to the handler. From 8c15f782ee9d6f9ad1890eada78afc0fcd7221a8 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Tue, 10 Jul 2018 15:57:30 +0200 Subject: [PATCH 19/22] fixed broken test, removed stale dependency Signed-off-by: Simon Kaufmann --- .../events/AbstractItemEventSubscriberOSGiTest.java | 8 +++++++- .../smarthome/core/thing/internal/ThingManager.java | 10 ---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/items/events/AbstractItemEventSubscriberOSGiTest.java b/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/items/events/AbstractItemEventSubscriberOSGiTest.java index 10d775cc637..2af8bac7e7b 100644 --- a/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/items/events/AbstractItemEventSubscriberOSGiTest.java +++ b/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/items/events/AbstractItemEventSubscriberOSGiTest.java @@ -24,6 +24,9 @@ import org.eclipse.smarthome.core.events.EventPublisher; import org.eclipse.smarthome.core.events.EventSubscriber; import org.eclipse.smarthome.core.items.ItemProvider; +import org.eclipse.smarthome.core.items.Metadata; +import org.eclipse.smarthome.core.items.MetadataKey; +import org.eclipse.smarthome.core.items.MetadataProvider; import org.eclipse.smarthome.core.library.items.SwitchItem; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.test.java.JavaOSGiTest; @@ -44,6 +47,7 @@ public class AbstractItemEventSubscriberOSGiTest extends JavaOSGiTest { private @Mock ItemProvider itemProvider; private ItemCommandEvent commandEvent; private ItemStateEvent updateEvent; + private @Mock MetadataProvider mockMetadataProvider; @Before public void setup() { @@ -68,7 +72,9 @@ protected void receiveUpdate(ItemStateEvent event) { }; registerService(itemEventSubscriber, EventSubscriber.class.getName()); - disableItemAutoUpdate(); + when(mockMetadataProvider.getAll()).thenReturn(Collections + .singletonList(new Metadata(new MetadataKey("autoupdate", ITEM_NAME), Boolean.toString(false), null))); + registerService(mockMetadataProvider); } @Test diff --git a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java index d7c1a2e39e7..b1dcedf797a 100644 --- a/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java +++ b/bundles/core/org.eclipse.smarthome.core.thing/src/main/java/org/eclipse/smarthome/core/thing/internal/ThingManager.java @@ -127,7 +127,6 @@ public class ThingManager implements ThingTracker, ThingTypeMigrationService, Re private EventPublisher eventPublisher; private CommunicationManager communicationManager; - private AutoUpdateManager autoUpdateManager; private ReadyService readyService; @@ -1203,13 +1202,4 @@ protected void unsetSafeCaller(SafeCaller safeCaller) { this.safeCaller = null; } - @Reference - public void setAutoUpdateManager(AutoUpdateManager autoUpdateManager) { - this.autoUpdateManager = autoUpdateManager; - } - - public void unsetAutoUpdateManager(AutoUpdateManager autoUpdateManager) { - this.autoUpdateManager = null; - } - } From e9736255224e8e0814ab09cc0549f78a4ec45343 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Wed, 11 Jul 2018 16:18:31 +0200 Subject: [PATCH 20/22] removed autoupdate from automation core test setup Signed-off-by: Simon Kaufmann --- .../org.eclipse.smarthome.automation.core.test/pom.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/bundles/automation/org.eclipse.smarthome.automation.core.test/pom.xml b/bundles/automation/org.eclipse.smarthome.automation.core.test/pom.xml index f9447c0c6c8..1a725b95cfd 100644 --- a/bundles/automation/org.eclipse.smarthome.automation.core.test/pom.xml +++ b/bundles/automation/org.eclipse.smarthome.automation.core.test/pom.xml @@ -67,11 +67,6 @@ org.eclipse.osgi 0.0.0 - - eclipse-plugin - org.eclipse.smarthome.core.autoupdate - 0.0.0 - eclipse-plugin @@ -135,11 +130,6 @@ 4 true - - org.eclipse.smarthome.core.autoupdate - 2 - true - From c6aafb91e97890f866a54376927aea94323df636 Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Mon, 13 Aug 2018 11:10:58 +0200 Subject: [PATCH 21/22] drop obsolete method from OSGiTest.java Signed-off-by: Simon Kaufmann --- .../org/eclipse/smarthome/test/OSGiTest.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/bundles/test/org.eclipse.smarthome.test/src/main/groovy/org/eclipse/smarthome/test/OSGiTest.java b/bundles/test/org.eclipse.smarthome.test/src/main/groovy/org/eclipse/smarthome/test/OSGiTest.java index 0e9b3bb034b..72d01249e3e 100644 --- a/bundles/test/org.eclipse.smarthome.test/src/main/groovy/org/eclipse/smarthome/test/OSGiTest.java +++ b/bundles/test/org.eclipse.smarthome.test/src/main/groovy/org/eclipse/smarthome/test/OSGiTest.java @@ -25,7 +25,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; -import org.eclipse.smarthome.core.autoupdate.AutoUpdateBindingConfigProvider; import org.eclipse.smarthome.core.i18n.LocaleProvider; import org.eclipse.smarthome.test.internal.java.MissingServiceAnalyzer; import org.eclipse.smarthome.test.storage.VolatileStorageService; @@ -310,19 +309,6 @@ public void unregisterMocks() { registeredServices.clear(); } - /** - * Inject a service to disable the auto-update feature. - */ - protected void disableItemAutoUpdate() { - registerService(new AutoUpdateBindingConfigProvider() { - - @Override - public Boolean autoUpdate(String itemName) { - return false; - } - }); - } - /** * When this method is called it waits until the condition is fulfilled or the timeout is reached. * The condition is specified by a closure, that must return a boolean object. When the condition is From c86d5e3d664875e9769e7e4232af1fad5e05455f Mon Sep 17 00:00:00 2001 From: Simon Kaufmann Date: Fri, 17 Aug 2018 15:52:31 +0200 Subject: [PATCH 22/22] fix prediction event payload bean type Signed-off-by: Simon Kaufmann --- .../core/events/ItemEventFactoryTest.java | 19 +++++++++++++++++++ .../core/items/events/ItemEventFactory.java | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/events/ItemEventFactoryTest.java b/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/events/ItemEventFactoryTest.java index 220e7d76b34..e310ac98358 100644 --- a/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/events/ItemEventFactoryTest.java +++ b/bundles/core/org.eclipse.smarthome.core.test/src/test/java/org/eclipse/smarthome/core/events/ItemEventFactoryTest.java @@ -22,6 +22,7 @@ import org.eclipse.smarthome.core.items.events.ItemCommandEvent; import org.eclipse.smarthome.core.items.events.ItemEventFactory; import org.eclipse.smarthome.core.items.events.ItemStateEvent; +import org.eclipse.smarthome.core.items.events.ItemStatePredictedEvent; import org.eclipse.smarthome.core.library.CoreItemFactory; import org.eclipse.smarthome.core.library.items.SwitchItem; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -49,11 +50,13 @@ public class ItemEventFactoryTest { private static final String ITEM_COMMAND_EVENT_TYPE = ItemCommandEvent.TYPE; private static final String ITEM_STATE_EVENT_TYPE = ItemStateEvent.TYPE; + private static final String ITEM_STATE_PREDICTED_EVENT_TYPE = ItemStatePredictedEvent.TYPE; private static final String ITEM_ADDED_EVENT_TYPE = ItemAddedEvent.TYPE; private static final String GROUPITEM_CHANGED_EVENT_TYPE = GroupItemStateChangedEvent.TYPE; private static final String ITEM_COMMAND_EVENT_TOPIC = "smarthome/items/" + ITEM_NAME + "/command"; private static final String ITEM_STATE_EVENT_TOPIC = "smarthome/items/" + ITEM_NAME + "/state"; + private static final String ITEM_STATE_PREDICTED_EVENT_TOPIC = "smarthome/items/" + ITEM_NAME + "/statepredicted"; private static final String ITEM_ADDED_EVENT_TOPIC = "smarthome/items/" + ITEM_NAME + "/added"; private static final String GROUPITEM_STATE_CHANGED_EVENT_TOPIC = "smarthome/items/" + GROUP_NAME + "/" + ITEM_NAME + "/statechanged"; @@ -65,6 +68,7 @@ public class ItemEventFactoryTest { private static final State ITEM_STATE = OnOffType.OFF; private static final State NEW_ITEM_STATE = OnOffType.ON; private static final String ITEM_STATE_EVENT_PAYLOAD = "{\"type\":\"OnOff\",\"value\":\"OFF\"}"; + private static final String ITEM_STATE_PREDICTED_EVENT_PAYLOAD = "{\"predictedType\":\"OnOff\",\"predictedValue\":\"OFF\",\"isConfirmation\":\"false\"}"; private static final String ITEM_ADDED_EVENT_PAYLOAD = new Gson().toJson(ItemDTOMapper.map(ITEM)); private static final String ITEM_STATE_CHANGED_EVENT_PAYLOAD = "{\"type\":\"OnOff\", \"value\": \"ON\", \"oldType\":\"OnOff\", \"oldValue\": \"OFF\"}"; @@ -166,6 +170,21 @@ public void testCreateEvent_ItemStateEvent_OnOffType() throws Exception { assertEquals(ITEM_STATE, itemStateEvent.getItemState()); } + @Test + public void testCreateEvent_ItemStatePredictedEvent_OnOffType() throws Exception { + Event event = factory.createEvent(ITEM_STATE_PREDICTED_EVENT_TYPE, ITEM_STATE_PREDICTED_EVENT_TOPIC, + ITEM_STATE_PREDICTED_EVENT_PAYLOAD, SOURCE); + + assertEquals(ItemStatePredictedEvent.class, event.getClass()); + ItemStatePredictedEvent itemStatePredictedEvent = (ItemStatePredictedEvent) event; + assertEquals(ITEM_STATE_PREDICTED_EVENT_TYPE, itemStatePredictedEvent.getType()); + assertEquals(ITEM_STATE_PREDICTED_EVENT_TOPIC, itemStatePredictedEvent.getTopic()); + assertEquals(ITEM_STATE_PREDICTED_EVENT_PAYLOAD, itemStatePredictedEvent.getPayload()); + assertEquals(ITEM_NAME, itemStatePredictedEvent.getItemName()); + assertEquals(OnOffType.class, itemStatePredictedEvent.getPredictedState().getClass()); + assertEquals(ITEM_STATE, itemStatePredictedEvent.getPredictedState()); + } + @Test public void testCreateStateEvent_OnOffType() { ItemStateEvent event = ItemEventFactory.createStateEvent(ITEM_NAME, ITEM_STATE, SOURCE); diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemEventFactory.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemEventFactory.java index 5b82ba6c07c..440dbfbf9a5 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemEventFactory.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/items/events/ItemEventFactory.java @@ -281,7 +281,8 @@ public static ItemStatePredictedEvent createStatePredictedEvent(String itemName, boolean isConfirmation) { assertValidArguments(itemName, state, "state"); String topic = buildTopic(ITEM_STATE_PREDICTED_EVENT_TOPIC, itemName); - ItemEventPayloadBean bean = new ItemEventPayloadBean(getStateType(state), state.toFullString()); + ItemStatePredictedEventPayloadBean bean = new ItemStatePredictedEventPayloadBean(getStateType(state), + state.toFullString(), isConfirmation); String payload = serializePayload(bean); return new ItemStatePredictedEvent(topic, payload, itemName, state, isConfirmation); }