]> git.basschouten.com Git - openhab-addons.git/commitdiff
[miio] Fix parsing error clear record (#17350)
authorMarcel <marcel@verpaalen.com>
Tue, 10 Sep 2024 10:40:52 +0000 (12:40 +0200)
committerGitHub <noreply@github.com>
Tue, 10 Sep 2024 10:40:52 +0000 (12:40 +0200)
* [miio] Fix parsing error clear record

Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>
bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandler.java
bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandlerTest.java [new file with mode: 0644]

index de5da91fa50a9ee545a3bb7b2aba23b22e22afd8..2b0efc2e322ad691d7bea19c607227f30787ce25 100644 (file)
@@ -98,6 +98,7 @@ import com.google.gson.JsonObject;
 public class MiIoVacuumHandler extends MiIoAbstractHandler {
     private final Logger logger = LoggerFactory.getLogger(MiIoVacuumHandler.class);
     private static final DateTimeFormatter DATEFORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss");
+    private static final DateTimeFormatter PARSER_TZ = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
     private static final Gson GSON = new GsonBuilder().serializeNulls().create();
     private final ChannelUID mapChannelUid;
 
@@ -504,6 +505,7 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
 
     private void updateHistoryRecordLegacy(JsonArray historyData) {
         HistoryRecordDTO historyRecord = new HistoryRecordDTO();
+
         for (int i = 0; i < historyData.size(); ++i) {
             try {
                 BigInteger value = historyData.get(i).getAsBigInteger();
@@ -511,12 +513,12 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
                     case 0:
                         historyRecord.setStart(ZonedDateTime
                                 .ofInstant(Instant.ofEpochSecond(value.longValue()), ZoneId.systemDefault())
-                                .toString());
+                                .format(PARSER_TZ));
                         break;
                     case 1:
                         historyRecord.setEnd(ZonedDateTime
                                 .ofInstant(Instant.ofEpochSecond(value.longValue()), ZoneId.systemDefault())
-                                .toString());
+                                .format(PARSER_TZ));
                         break;
                     case 2:
                         historyRecord.setDuration(value.intValue());
@@ -549,14 +551,14 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
     private void updateHistoryRecord(HistoryRecordDTO historyRecordDTO) {
         JsonObject historyRecord = GSON.toJsonTree(historyRecordDTO).getAsJsonObject();
         if (historyRecordDTO.getStart() != null) {
-            historyRecord.addProperty("start", historyRecordDTO.getStart().split("\\+")[0].split("\\-")[0]);
-            updateState(CHANNEL_HISTORY_START_TIME,
-                    new DateTimeType(historyRecordDTO.getStart().split("\\+")[0].split("\\-")[0]));
+            DateTimeType start = new DateTimeType(historyRecordDTO.getStart());
+            historyRecord.addProperty("start", start.toLocaleZone().format(null));
+            updateState(CHANNEL_HISTORY_START_TIME, start);
         }
         if (historyRecordDTO.getEnd() != null) {
-            historyRecord.addProperty("end", historyRecordDTO.getEnd().split("\\+")[0].split("\\-")[0]);
-            updateState(CHANNEL_HISTORY_END_TIME,
-                    new DateTimeType(historyRecordDTO.getEnd().split("\\+")[0].split("\\-")[0]));
+            DateTimeType end = new DateTimeType(historyRecordDTO.getEnd());
+            historyRecord.addProperty("end", end.toLocaleZone().format(null));
+            updateState(CHANNEL_HISTORY_END_TIME, end);
         }
         if (historyRecordDTO.getDuration() != null) {
             long duration = TimeUnit.SECONDS.toMinutes(historyRecordDTO.getDuration().longValue());
diff --git a/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandlerTest.java b/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandlerTest.java
new file mode 100644 (file)
index 0000000..57f4144
--- /dev/null
@@ -0,0 +1,171 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.miio.internal.handler;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.concurrent.TimeUnit;
+
+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.Timeout;
+import org.junit.jupiter.api.Timeout.ThreadMode;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.openhab.binding.miio.internal.MiIoBindingConstants;
+import org.openhab.binding.miio.internal.MiIoCommand;
+import org.openhab.binding.miio.internal.MiIoSendCommand;
+import org.openhab.binding.miio.internal.basic.MiIoDatabaseWatchService;
+import org.openhab.binding.miio.internal.cloud.CloudConnector;
+import org.openhab.binding.miio.internal.cloud.MiCloudException;
+import org.openhab.binding.miio.internal.transport.MiIoAsyncCommunication;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.i18n.LocaleProvider;
+import org.openhab.core.i18n.TranslationProvider;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingStatusInfo;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandlerCallback;
+import org.openhab.core.thing.type.ChannelTypeRegistry;
+
+import com.google.gson.JsonParser;
+
+/**
+ * Test case for {@link MiIoVacuumHandler}
+ *
+ * @author Marcel Verpaalen - Initial contribution
+ *
+ */
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+@NonNullByDefault
+public class MiIoVacuumHandlerTest {
+
+    private @NonNullByDefault({}) MiIoVacuumHandler miIoHandler;
+    private @Mock @NonNullByDefault({}) ThingHandlerCallback callback;
+
+    private @Mock @NonNullByDefault({}) CloudConnector cloudConnector;
+    private @Mock @NonNullByDefault({}) MiIoDatabaseWatchService miIoDatabaseWatchService;
+    private @Mock @NonNullByDefault({}) ChannelTypeRegistry channelTypeRegistry;
+    private @Mock @NonNullByDefault({}) Thing thing;
+    private @Mock @NonNullByDefault({}) MiIoAsyncCommunication connection;
+    private @NonNullByDefault({}) @Mock TranslationProvider translationProvider;
+    private @NonNullByDefault({}) @Mock LocaleProvider localeProvider;
+
+    private final Configuration configuration = new Configuration();
+    private ThingUID thingUID = new ThingUID(MiIoBindingConstants.THING_TYPE_VACUUM, "TestThing");
+
+    @BeforeEach
+    public void setUp() throws IOException, MiCloudException {
+        configuration.put(MiIoBindingConstants.PROPERTY_HOST_IP, "localhost");
+        configuration.put(MiIoBindingConstants.PROPERTY_TOKEN, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+        configuration.put(MiIoBindingConstants.PROPERTY_DID, "AABBCCDDEEFF");
+        configuration.put(MiIoBindingConstants.PROPERTY_CLOUDSERVER, "fake");
+        configuration.put("communication", "cloud");
+
+        when(thing.getConfiguration()).thenReturn(configuration);
+        when(thing.getUID()).thenReturn(thingUID);
+        when(thing.getThingTypeUID()).thenReturn(MiIoBindingConstants.THING_TYPE_VACUUM);
+        when(cloudConnector.sendRPCCommand(any(), any(), any())).thenReturn("{\"result\":\"triggerError\"}");
+        lenient().when(callback.isChannelLinked(any())).thenReturn(true);
+
+        miIoHandler = new MiIoVacuumHandler(thing, miIoDatabaseWatchService, cloudConnector, channelTypeRegistry,
+                translationProvider, localeProvider);
+
+        miIoHandler.setCallback(callback);
+    }
+
+    @AfterEach
+    public void after() {
+        miIoHandler.dispose();
+    }
+
+    @Test
+    public void testInitializeShouldCallTheCallback() throws InterruptedException {
+        miIoHandler.initialize();
+        ArgumentCaptor<ThingStatusInfo> statusInfoCaptor = ArgumentCaptor.forClass(ThingStatusInfo.class);
+        verify(callback).statusUpdated(eq(thing), statusInfoCaptor.capture());
+        ThingStatusInfo thingStatusInfo = statusInfoCaptor.getValue();
+        assertEquals(ThingStatus.OFFLINE, thingStatusInfo.getStatus(), "Device should be OFFLINE");
+    }
+
+    @Test
+    @Timeout(value = 30, unit = TimeUnit.SECONDS, threadMode = ThreadMode.SEPARATE_THREAD)
+    public void TestCleanRecord() {
+        miIoHandler.initialize();
+        // prepare a CLEAN_RECORD_GET response object
+        String cmdString = "{\"id\":7028,\"method\":\"get_clean_record\",\"params\":[1699081963]}";
+        String jsonResponseTxt = "{\"result\":[[1724174413,1724174459,246,770000,0,0,2,3,60]],\"id\":7028}";
+
+        MiIoSendCommand response = new MiIoSendCommand(13, MiIoCommand.CLEAN_RECORD_GET,
+                JsonParser.parseString(cmdString).getAsJsonObject(), "", "");
+        response.setResponse(JsonParser.parseString(jsonResponseTxt).getAsJsonObject());
+        miIoHandler.onMessageReceived(response);
+
+        verify(callback, description("Test the start time parsing")).stateUpdated(
+                eq(new ChannelUID(thingUID, MiIoBindingConstants.CHANNEL_HISTORY_START_TIME)),
+                eq(new DateTimeType(ZonedDateTime.parse("2024-08-20T19:20:13+02:00")).toZone(ZoneId.systemDefault())));
+
+        verify(callback, description("Test the end time parsing")).stateUpdated(
+                eq(new ChannelUID(thingUID, MiIoBindingConstants.CHANNEL_HISTORY_END_TIME)),
+                eq(new DateTimeType(ZonedDateTime.parse("2024-08-20T19:20:59+02:00")).toZone(ZoneId.systemDefault())));
+
+        verify(callback, description("Test the duration parsing")).stateUpdated(
+                eq(new ChannelUID(thingUID, MiIoBindingConstants.CHANNEL_HISTORY_DURATION)),
+                eq(new QuantityType<>(4, Units.MINUTE)));
+    }
+
+    @Test
+    @Timeout(value = 30, unit = TimeUnit.SECONDS, threadMode = ThreadMode.SEPARATE_THREAD)
+    public void TestCleanSummary() {
+        miIoHandler.initialize();
+
+        ThingStatusInfo ts = new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, "I fake to be online");
+        when(thing.getStatusInfo()).thenReturn(ts);
+
+        // prepare a CLEAN_SUMMARY_GET response object
+        String cmdString = "{\"id\":114,\"method\":\"get_clean_summary\",\"params\":[]}";
+        String jsonResponseTxt = "{\"id\":114,\"result\":{\"clean_time\":109968,\"clean_area\":1694875000,\"clean_count\":51,\"dust_collection_count\":48,\"records\":[1699081963,1698999875,1698126572,1697463736,1697031817,1696486642,1696320557,1696253060,1695833343,1695821201,1695619374,1695476013,1695457865,1695274110,1695014622,1694876238,1694860994,1694755927,1694526730,1694237806]}}";
+
+        MiIoSendCommand response = new MiIoSendCommand(13, MiIoCommand.CLEAN_SUMMARY_GET,
+                JsonParser.parseString(cmdString).getAsJsonObject(), "", "");
+        response.setResponse(JsonParser.parseString(jsonResponseTxt).getAsJsonObject());
+        miIoHandler.onMessageReceived(response);
+
+        verify(callback, description("Test clean time")).stateUpdated(
+                eq(new ChannelUID(thingUID, MiIoBindingConstants.CHANNEL_HISTORY_TOTALTIME)),
+                eq(new QuantityType<>(TimeUnit.MINUTES.convert(109968, TimeUnit.SECONDS), Units.MINUTE)));
+
+        verify(callback, description("Test the area parsing")).stateUpdated(
+                eq(new ChannelUID(thingUID, MiIoBindingConstants.CHANNEL_HISTORY_TOTALAREA)),
+                eq(new QuantityType<>(1694.875, SIUnits.SQUARE_METRE)));
+    }
+}