[uom] Add unit metadata for NumberItem (#3481)

* Add defaultUnit metadata for NumberItem

Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
J-N-K
2023-05-09 22:42:25 +02:00
committed by GitHub
parent 67b80af872
commit 9ef076dc6a
32 changed files with 941 additions and 716 deletions
@@ -116,4 +116,9 @@ Fragment-Host: org.openhab.core.model.script
junit-platform-commons;version='[1.9.2,1.9.3)',\
junit-platform-engine;version='[1.9.2,1.9.3)',\
junit-platform-launcher;version='[1.9.2,1.9.3)',\
org.openhab.core.model.thing.runtime;version='[4.0.0,4.0.1)'
org.openhab.core.model.thing.runtime;version='[4.0.0,4.0.1)',\
net.bytebuddy.byte-buddy;version='[1.12.19,1.12.20)',\
net.bytebuddy.byte-buddy-agent;version='[1.12.19,1.12.20)',\
org.mockito.junit-jupiter;version='[4.11.0,4.11.1)',\
org.mockito.mockito-core;version='[4.11.0,4.11.1)',\
org.objenesis;version='[3.3.0,3.3.1)'
@@ -15,6 +15,7 @@ package org.openhab.core.model.script.engine;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
import java.util.Collection;
import java.util.List;
@@ -26,8 +27,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.openhab.core.common.registry.ProviderChangeListener;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemProvider;
import org.openhab.core.items.ItemRegistry;
@@ -48,6 +55,8 @@ import org.openhab.core.types.State;
* @author Henning Treu - Initial contribution
*/
@NonNullByDefault
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class ScriptEngineOSGiTest extends JavaOSGiTest {
private static final String ITEM_NAME = "Switch1";
@@ -58,9 +67,13 @@ public class ScriptEngineOSGiTest extends JavaOSGiTest {
private @NonNullByDefault({}) ItemProvider itemProvider;
private @NonNullByDefault({}) ItemRegistry itemRegistry;
private @NonNullByDefault({}) ScriptEngine scriptEngine;
private @Mock @NonNullByDefault({}) UnitProvider unitProviderMock;
@BeforeEach
public void setup() {
when(unitProviderMock.getUnit(Temperature.class)).thenReturn(SIUnits.CELSIUS);
when(unitProviderMock.getUnit(Length.class)).thenReturn(SIUnits.METRE);
registerVolatileStorageService();
EventPublisher eventPublisher = event -> {
@@ -351,7 +364,7 @@ public class ScriptEngineOSGiTest extends JavaOSGiTest {
}
private Item createNumberItem(String numberItemName, Class<?> dimension) {
return new NumberItem("Number:" + dimension.getSimpleName(), numberItemName);
return new NumberItem("Number:" + dimension.getSimpleName(), numberItemName, unitProviderMock);
}
@SuppressWarnings("unchecked")
@@ -17,6 +17,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
import static org.openhab.core.library.unit.Units.ONE;
import java.util.HashSet;
import java.util.LinkedList;
@@ -64,6 +65,7 @@ import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.RawType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
@@ -796,7 +798,6 @@ public class GroupItemOSGiTest extends JavaOSGiTest {
gfDTO.name = "sum";
GroupFunction function = groupFunctionHelper.createGroupFunction(gfDTO, baseItem);
GroupItem groupItem = new GroupItem("number", baseItem, function);
groupItem.setUnitProvider(unitProviderMock);
NumberItem celsius = createNumberItem("C", Temperature.class, new QuantityType<>("23 °C"));
groupItem.addMember(celsius);
@@ -820,12 +821,14 @@ public class GroupItemOSGiTest extends JavaOSGiTest {
@Test
public void assertThatNumberGroupItemWithDifferentDimensionsCalculatesCorrectState() {
when(unitProviderMock.getUnit(Temperature.class)).thenReturn(SIUnits.CELSIUS);
when(unitProviderMock.getUnit(Pressure.class)).thenReturn(SIUnits.PASCAL);
when(unitProviderMock.getUnit(Dimensionless.class)).thenReturn(ONE);
NumberItem baseItem = createNumberItem("baseItem", Temperature.class, UnDefType.NULL);
GroupFunctionDTO gfDTO = new GroupFunctionDTO();
gfDTO.name = "sum";
GroupFunction function = groupFunctionHelper.createGroupFunction(gfDTO, baseItem);
GroupItem groupItem = new GroupItem("number", baseItem, function);
groupItem.setUnitProvider(unitProviderMock);
groupItem.setItemStateConverter(itemStateConverter);
NumberItem celsius = createNumberItem("C", Temperature.class, new QuantityType<>("23 °C"));
@@ -844,8 +847,8 @@ public class GroupItemOSGiTest extends JavaOSGiTest {
}
private NumberItem createNumberItem(String name, Class<? extends Quantity<?>> dimension, State state) {
NumberItem item = new NumberItem(CoreItemFactory.NUMBER + ":" + dimension.getSimpleName(), name);
item.setUnitProvider(unitProviderMock);
NumberItem item = new NumberItem(CoreItemFactory.NUMBER + ":" + dimension.getSimpleName(), name,
unitProviderMock);
item.setState(state);
return item;
@@ -77,6 +77,7 @@ public class ItemRegistryImplTest extends JavaTest {
private @NonNullByDefault({}) ManagedItemProvider itemProvider;
private @Mock @NonNullByDefault({}) EventPublisher eventPublisherMock;
private @Mock @NonNullByDefault({}) UnitProvider unitProviderMock;
@BeforeEach
public void beforeEach() {
@@ -92,7 +93,7 @@ public class ItemRegistryImplTest extends JavaTest {
// setup ManageItemProvider with necessary dependencies:
itemProvider = new ManagedItemProvider(new VolatileStorageService(),
new ItemBuilderFactoryImpl(new CoreItemFactory()));
new ItemBuilderFactoryImpl(new CoreItemFactory(unitProviderMock)));
itemProvider.add(new SwitchItem(ITEM_NAME));
itemProvider.add(cameraItem1);
@@ -107,7 +108,6 @@ public class ItemRegistryImplTest extends JavaTest {
setManagedProvider(itemProvider);
setEventPublisher(ItemRegistryImplTest.this.eventPublisherMock);
setStateDescriptionService(mock(StateDescriptionService.class));
setUnitProvider(mock(UnitProvider.class));
setItemStateConverter(mock(ItemStateConverter.class));
}
};
@@ -369,13 +369,11 @@ public class ItemRegistryImplTest extends JavaTest {
assertNotNull(item.eventPublisher);
assertNotNull(item.itemStateConverter);
assertNotNull(item.unitProvider);
itemProvider.update(new SwitchItem("Item1"));
assertNull(item.eventPublisher);
assertNull(item.itemStateConverter);
assertNull(item.unitProvider);
assertEquals(0, item.listeners.size());
}
@@ -391,18 +389,6 @@ public class ItemRegistryImplTest extends JavaTest {
verify(baseItem).setStateDescriptionService(any(StateDescriptionService.class));
}
@Test
public void assertUnitProviderGetsInjected() {
GenericItem item = spy(new SwitchItem("Item1"));
NumberItem baseItem = spy(new NumberItem("baseItem"));
GenericItem group = new GroupItem("Group", baseItem);
itemProvider.add(item);
itemProvider.add(group);
verify(item).setUnitProvider(any(UnitProvider.class));
verify(baseItem).setUnitProvider(any(UnitProvider.class));
}
@Test
public void assertCommandDescriptionServiceGetsInjected() {
GenericItem item = spy(new SwitchItem("Item1"));
@@ -40,6 +40,8 @@ import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.items.ItemStateConverter;
import org.openhab.core.items.Metadata;
import org.openhab.core.items.MetadataKey;
import org.openhab.core.items.events.ItemCommandEvent;
import org.openhab.core.items.events.ItemEventFactory;
import org.openhab.core.library.CoreItemFactory;
@@ -50,8 +52,8 @@ import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.service.StateDescriptionService;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
@@ -81,7 +83,6 @@ import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.StateDescriptionFragmentBuilder;
/**
*
@@ -103,21 +104,27 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
}
}
private static final UnitProvider unitProviderMock = mock(UnitProvider.class);
private static final String EVENT = "event";
private static final String ITEM_NAME_1 = "testItem1";
private static final String ITEM_NAME_2 = "testItem2";
private static final String ITEM_NAME_3 = "testItem3";
private static final String ITEM_NAME_4 = "testItem4";
private static final String ITEM_NAME_5 = "testItem5";
private static final SwitchItem ITEM_1 = new SwitchItem(ITEM_NAME_1);
private static final SwitchItem ITEM_2 = new SwitchItem(ITEM_NAME_2);
private static final NumberItem ITEM_3 = new NumberItem(ITEM_NAME_3);
private static final NumberItem ITEM_4 = new NumberItem(ITEM_NAME_4);
private static NumberItem ITEM_5 = new NumberItem(ITEM_NAME_5); // will be replaced later by dimension item
private static final ThingTypeUID THING_TYPE_UID = new ThingTypeUID("test", "type");
private static final ThingUID THING_UID = new ThingUID("test", "thing");
private static final ChannelUID STATE_CHANNEL_UID_1 = new ChannelUID(THING_UID, "state-channel1");
private static final ChannelUID STATE_CHANNEL_UID_2 = new ChannelUID(THING_UID, "state-channel2");
private static final ChannelUID STATE_CHANNEL_UID_3 = new ChannelUID(THING_UID, "state-channel3");
private static final ChannelUID STATE_CHANNEL_UID_4 = new ChannelUID(THING_UID, "state-channel4");
private static final ChannelUID STATE_CHANNEL_UID_5 = new ChannelUID(THING_UID, "state-channel5");
private static final ChannelTypeUID CHANNEL_TYPE_UID_4 = new ChannelTypeUID("test", "channeltype");
private static final ChannelUID TRIGGER_CHANNEL_UID_1 = new ChannelUID(THING_UID, "trigger-channel1");
private static final ChannelUID TRIGGER_CHANNEL_UID_2 = new ChannelUID(THING_UID, "trigger-channel2");
@@ -126,6 +133,7 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
private static final ItemChannelLink LINK_2_S2 = new ItemChannelLink(ITEM_NAME_2, STATE_CHANNEL_UID_2);
private static final ItemChannelLink LINK_3_S3 = new ItemChannelLink(ITEM_NAME_3, STATE_CHANNEL_UID_3);
private static final ItemChannelLink LINK_4_S4 = new ItemChannelLink(ITEM_NAME_4, STATE_CHANNEL_UID_4);
private static final ItemChannelLink LINK_5_S5 = new ItemChannelLink(ITEM_NAME_5, STATE_CHANNEL_UID_5);
private static final ItemChannelLink LINK_1_T1 = new ItemChannelLink(ITEM_NAME_1, TRIGGER_CHANNEL_UID_1);
private static final ItemChannelLink LINK_1_T2 = new ItemChannelLink(ITEM_NAME_1, TRIGGER_CHANNEL_UID_2);
private static final ItemChannelLink LINK_2_T2 = new ItemChannelLink(ITEM_NAME_2, TRIGGER_CHANNEL_UID_2);
@@ -135,6 +143,7 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
ChannelBuilder.create(STATE_CHANNEL_UID_3, "Number:Temperature").withKind(ChannelKind.STATE).build(),
ChannelBuilder.create(STATE_CHANNEL_UID_4, CoreItemFactory.NUMBER).withKind(ChannelKind.STATE)
.withType(CHANNEL_TYPE_UID_4).build(),
ChannelBuilder.create(STATE_CHANNEL_UID_5, "Number:Temperature").withKind(ChannelKind.STATE).build(),
ChannelBuilder.create(TRIGGER_CHANNEL_UID_1).withKind(ChannelKind.TRIGGER).build(),
ChannelBuilder.create(TRIGGER_CHANNEL_UID_2).withKind(ChannelKind.TRIGGER).build()).build();
@@ -158,6 +167,9 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
@BeforeEach
public void beforeEach() {
when(unitProviderMock.getUnit(Temperature.class)).thenReturn(SIUnits.CELSIUS);
ITEM_5 = new NumberItem("Number:Temperature", ITEM_NAME_5, unitProviderMock);
safeCaller = getService(SafeCaller.class);
assertNotNull(safeCaller);
@@ -166,7 +178,8 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
assertNotNull(profileFactory);
manager = new CommunicationManager(autoUpdateManagerMock, channelTypeRegistryMock, profileFactory, iclRegistry,
itemRegistryMock, itemStateConverterMock, eventPublisherMock, safeCaller, thingRegistryMock);
itemRegistryMock, itemStateConverterMock, eventPublisherMock, safeCaller, thingRegistryMock,
unitProviderMock);
doAnswer(invocation -> {
switch (((Channel) invocation.getArguments()[0]).getKind()) {
@@ -205,7 +218,8 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
@Override
public Collection<ItemChannelLink> getAll() {
return List.of(LINK_1_S1, LINK_1_S2, LINK_2_S2, LINK_1_T1, LINK_1_T2, LINK_2_T2, LINK_3_S3, LINK_4_S4);
return List.of(LINK_1_S1, LINK_1_S2, LINK_2_S2, LINK_1_T1, LINK_1_T2, LINK_2_T2, LINK_3_S3, LINK_4_S4,
LINK_5_S5);
}
});
@@ -213,6 +227,7 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
when(itemRegistryMock.get(eq(ITEM_NAME_2))).thenReturn(ITEM_2);
when(itemRegistryMock.get(eq(ITEM_NAME_3))).thenReturn(ITEM_3);
when(itemRegistryMock.get(eq(ITEM_NAME_4))).thenReturn(ITEM_4);
when(itemRegistryMock.get(eq(ITEM_NAME_5))).thenReturn(ITEM_5);
ChannelType channelType4 = mock(ChannelType.class);
when(channelType4.getItemType()).thenReturn("Number:Temperature");
@@ -222,12 +237,8 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
THING.setHandler(thingHandlerMock);
when(thingRegistryMock.get(eq(THING_UID))).thenReturn(THING);
manager.addItemFactory(new CoreItemFactory());
UnitProvider unitProvider = mock(UnitProvider.class);
when(unitProvider.getUnit(Temperature.class)).thenReturn(SIUnits.CELSIUS);
ITEM_3.setUnitProvider(unitProvider);
ITEM_4.setUnitProvider(unitProvider);
manager.addItemFactory(new CoreItemFactory(unitProviderMock));
}
@Test
@@ -284,7 +295,7 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
@Test
public void testItemCommandEventDecimal2Quantity() {
// Take unit from accepted item type (see channel built from STATE_CHANNEL_UID_3)
manager.receive(ItemEventFactory.createCommandEvent(ITEM_NAME_3, DecimalType.valueOf("20")));
manager.receive(ItemEventFactory.createCommandEvent(ITEM_NAME_5, DecimalType.valueOf("20")));
waitForAssert(() -> {
verify(stateProfileMock).onCommandFromItem(eq(QuantityType.valueOf("20 °C")));
});
@@ -294,33 +305,16 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
@Test
public void testItemCommandEventDecimal2Quantity2() {
// Take unit from state description
StateDescriptionService stateDescriptionService = mock(StateDescriptionService.class);
when(stateDescriptionService.getStateDescription(ITEM_NAME_3, null)).thenReturn(
StateDescriptionFragmentBuilder.create().withPattern("%.1f °F").build().toStateDescription());
ITEM_3.setStateDescriptionService(stateDescriptionService);
MetadataKey key = new MetadataKey(NumberItem.UNIT_METADATA_NAMESPACE, ITEM_NAME_5);
Metadata metadata = new Metadata(key, ImperialUnits.FAHRENHEIT.toString(), null);
ITEM_5.addedMetadata(metadata);
manager.receive(ItemEventFactory.createCommandEvent(ITEM_NAME_3, DecimalType.valueOf("20")));
manager.receive(ItemEventFactory.createCommandEvent(ITEM_NAME_5, DecimalType.valueOf("20")));
waitForAssert(() -> {
verify(stateProfileMock).onCommandFromItem(eq(QuantityType.valueOf("20 °F")));
});
verifyNoMoreInteractions(stateProfileMock);
verifyNoMoreInteractions(triggerProfileMock);
ITEM_3.setStateDescriptionService(null);
}
@Test
public void testItemCommandEventDecimal2QuantityChannelType() {
// The command is sent to an item w/o dimension defined and the channel is legacy (created from a ThingType
// definition before UoM was introduced to the binding). The dimension information might now be defined on the
// current ThingType.
manager.receive(ItemEventFactory.createCommandEvent(ITEM_NAME_4, DecimalType.valueOf("20")));
waitForAssert(() -> {
verify(stateProfileMock).onCommandFromItem(eq(QuantityType.valueOf("20 °C")));
});
verifyNoMoreInteractions(stateProfileMock);
verifyNoMoreInteractions(triggerProfileMock);
}
@Test