2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.mybmw.internal.dto;
15 import static org.junit.jupiter.api.Assertions.assertEquals;
16 import static org.junit.jupiter.api.Assertions.assertFalse;
17 import static org.junit.jupiter.api.Assertions.assertNotNull;
18 import static org.junit.jupiter.api.Assertions.assertTrue;
19 import static org.openhab.binding.mybmw.internal.MyBMWConstants.ADDRESS;
20 import static org.openhab.binding.mybmw.internal.MyBMWConstants.CHANNEL_GROUP_CHARGE_PROFILE;
21 import static org.openhab.binding.mybmw.internal.MyBMWConstants.CHANNEL_GROUP_CHECK_CONTROL;
22 import static org.openhab.binding.mybmw.internal.MyBMWConstants.CHANNEL_GROUP_RANGE;
23 import static org.openhab.binding.mybmw.internal.MyBMWConstants.CHANNEL_GROUP_SERVICE;
24 import static org.openhab.binding.mybmw.internal.MyBMWConstants.CHANNEL_GROUP_STATUS;
25 import static org.openhab.binding.mybmw.internal.MyBMWConstants.CHARGE_REMAINING;
26 import static org.openhab.binding.mybmw.internal.MyBMWConstants.CHARGE_STATUS;
27 import static org.openhab.binding.mybmw.internal.MyBMWConstants.CHECK_CONTROL;
28 import static org.openhab.binding.mybmw.internal.MyBMWConstants.DATE;
29 import static org.openhab.binding.mybmw.internal.MyBMWConstants.DETAILS;
30 import static org.openhab.binding.mybmw.internal.MyBMWConstants.DOORS;
31 import static org.openhab.binding.mybmw.internal.MyBMWConstants.DOOR_DRIVER_FRONT;
32 import static org.openhab.binding.mybmw.internal.MyBMWConstants.DOOR_DRIVER_REAR;
33 import static org.openhab.binding.mybmw.internal.MyBMWConstants.DOOR_PASSENGER_FRONT;
34 import static org.openhab.binding.mybmw.internal.MyBMWConstants.DOOR_PASSENGER_REAR;
35 import static org.openhab.binding.mybmw.internal.MyBMWConstants.ESTIMATED_FUEL_L_100KM;
36 import static org.openhab.binding.mybmw.internal.MyBMWConstants.ESTIMATED_FUEL_MPG;
37 import static org.openhab.binding.mybmw.internal.MyBMWConstants.FRONT_LEFT_CURRENT;
38 import static org.openhab.binding.mybmw.internal.MyBMWConstants.FRONT_LEFT_TARGET;
39 import static org.openhab.binding.mybmw.internal.MyBMWConstants.FRONT_RIGHT_CURRENT;
40 import static org.openhab.binding.mybmw.internal.MyBMWConstants.FRONT_RIGHT_TARGET;
41 import static org.openhab.binding.mybmw.internal.MyBMWConstants.GPS;
42 import static org.openhab.binding.mybmw.internal.MyBMWConstants.HEADING;
43 import static org.openhab.binding.mybmw.internal.MyBMWConstants.HOME_DISTANCE;
44 import static org.openhab.binding.mybmw.internal.MyBMWConstants.HOOD;
45 import static org.openhab.binding.mybmw.internal.MyBMWConstants.LAST_FETCHED;
46 import static org.openhab.binding.mybmw.internal.MyBMWConstants.LAST_UPDATE;
47 import static org.openhab.binding.mybmw.internal.MyBMWConstants.LOCK;
48 import static org.openhab.binding.mybmw.internal.MyBMWConstants.MILEAGE;
49 import static org.openhab.binding.mybmw.internal.MyBMWConstants.NAME;
50 import static org.openhab.binding.mybmw.internal.MyBMWConstants.PLUG_CONNECTION;
51 import static org.openhab.binding.mybmw.internal.MyBMWConstants.RANGE_ELECTRIC;
52 import static org.openhab.binding.mybmw.internal.MyBMWConstants.RANGE_FUEL;
53 import static org.openhab.binding.mybmw.internal.MyBMWConstants.RANGE_HYBRID;
54 import static org.openhab.binding.mybmw.internal.MyBMWConstants.RANGE_RADIUS_ELECTRIC;
55 import static org.openhab.binding.mybmw.internal.MyBMWConstants.RANGE_RADIUS_FUEL;
56 import static org.openhab.binding.mybmw.internal.MyBMWConstants.RANGE_RADIUS_HYBRID;
57 import static org.openhab.binding.mybmw.internal.MyBMWConstants.RAW;
58 import static org.openhab.binding.mybmw.internal.MyBMWConstants.REAR_LEFT_CURRENT;
59 import static org.openhab.binding.mybmw.internal.MyBMWConstants.REAR_LEFT_TARGET;
60 import static org.openhab.binding.mybmw.internal.MyBMWConstants.REAR_RIGHT_CURRENT;
61 import static org.openhab.binding.mybmw.internal.MyBMWConstants.REAR_RIGHT_TARGET;
62 import static org.openhab.binding.mybmw.internal.MyBMWConstants.REMAINING_FUEL;
63 import static org.openhab.binding.mybmw.internal.MyBMWConstants.SERVICE_DATE;
64 import static org.openhab.binding.mybmw.internal.MyBMWConstants.SERVICE_MILEAGE;
65 import static org.openhab.binding.mybmw.internal.MyBMWConstants.SEVERITY;
66 import static org.openhab.binding.mybmw.internal.MyBMWConstants.SOC;
67 import static org.openhab.binding.mybmw.internal.MyBMWConstants.SUNROOF;
68 import static org.openhab.binding.mybmw.internal.MyBMWConstants.TRUNK;
69 import static org.openhab.binding.mybmw.internal.MyBMWConstants.WINDOWS;
70 import static org.openhab.binding.mybmw.internal.MyBMWConstants.WINDOW_DOOR_DRIVER_FRONT;
71 import static org.openhab.binding.mybmw.internal.MyBMWConstants.WINDOW_DOOR_DRIVER_REAR;
72 import static org.openhab.binding.mybmw.internal.MyBMWConstants.WINDOW_DOOR_PASSENGER_FRONT;
73 import static org.openhab.binding.mybmw.internal.MyBMWConstants.WINDOW_DOOR_PASSENGER_REAR;
75 import java.time.ZoneId;
76 import java.util.HashMap;
77 import java.util.List;
80 import javax.measure.Unit;
81 import javax.measure.quantity.Length;
83 import org.eclipse.jdt.annotation.NonNullByDefault;
84 import org.eclipse.jdt.annotation.Nullable;
85 import org.openhab.binding.mybmw.internal.MyBMWConstants.VehicleType;
86 import org.openhab.binding.mybmw.internal.dto.vehicle.RequiredService;
87 import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleState;
88 import org.openhab.binding.mybmw.internal.dto.vehicle.VehicleStateContainer;
89 import org.openhab.binding.mybmw.internal.handler.VehicleHandlerTest;
90 import org.openhab.binding.mybmw.internal.handler.backend.JsonStringDeserializer;
91 import org.openhab.binding.mybmw.internal.utils.Constants;
92 import org.openhab.binding.mybmw.internal.utils.Converter;
93 import org.openhab.binding.mybmw.internal.utils.VehicleStatusUtils;
94 import org.openhab.core.library.types.DateTimeType;
95 import org.openhab.core.library.types.DecimalType;
96 import org.openhab.core.library.types.PointType;
97 import org.openhab.core.library.types.QuantityType;
98 import org.openhab.core.library.types.StringType;
99 import org.openhab.core.library.unit.SIUnits;
100 import org.openhab.core.library.unit.Units;
101 import org.openhab.core.thing.ChannelUID;
102 import org.openhab.core.types.State;
103 import org.openhab.core.types.UnDefType;
106 * The {@link StatusWrapper} tests stored fingerprint responses from BMW API
108 * @author Bernd Weymann - Initial contribution
109 * @author Martin Grassl - updates for v2 API
110 * @author Mark Herwege - remaining charging time test
113 @SuppressWarnings("null")
114 public class StatusWrapper {
115 private static final Unit<Length> KILOMETRE = Constants.KILOMETRE_UNIT;
117 private VehicleState vehicleState;
118 private boolean isElectric;
119 private boolean hasFuel;
120 private boolean isHybrid;
122 private Map<String, State> specialHandlingMap = new HashMap<String, State>();
124 public StatusWrapper(String type, String statusJson) {
125 hasFuel = type.equals(VehicleType.CONVENTIONAL.toString()) || type.equals(VehicleType.PLUGIN_HYBRID.toString())
126 || type.equals(VehicleType.ELECTRIC_REX.toString()) || type.equals(VehicleType.MILD_HYBRID.toString());
127 isElectric = type.equals(VehicleType.PLUGIN_HYBRID.toString())
128 || type.equals(VehicleType.ELECTRIC_REX.toString()) || type.equals(VehicleType.ELECTRIC.toString());
129 isHybrid = hasFuel && isElectric;
130 VehicleStateContainer vehicleStateContainer = JsonStringDeserializer.getVehicleState(statusJson);
131 vehicleState = vehicleStateContainer.getState();
135 * Test results automatically against json values
141 public boolean checkResults(@Nullable List<ChannelUID> channels, @Nullable List<State> states) {
142 assertNotNull(channels);
143 assertNotNull(states);
144 assertTrue(channels.size() == states.size(), "Same list sizes");
145 for (int i = 0; i < channels.size(); i++) {
146 checkResult(channels.get(i), states.get(i));
152 * Add a specific check for a value e.g. hard coded "Upcoming Service" in order to check the right ordering
157 public StatusWrapper append(Map<String, State> compareMap) {
158 specialHandlingMap.putAll(compareMap);
162 @SuppressWarnings({ "unchecked", "rawtypes" })
163 private void checkResult(ChannelUID channelUID, State state) {
164 String cUid = channelUID.getIdWithoutGroup();
165 String gUid = channelUID.getGroupId();
166 QuantityType<Length> qt;
172 Unit<Length> wantedUnit = KILOMETRE;
176 case CHANNEL_GROUP_RANGE:
177 if (!state.equals(UnDefType.UNDEF)) {
178 assertTrue(state instanceof QuantityType);
179 qt = ((QuantityType) state);
180 assertEquals(qt.intValue(), vehicleState.getCurrentMileage(), "Mileage");
182 assertEquals(Constants.INT_UNDEF, vehicleState.getCurrentMileage(), "Mileage undefined");
185 case CHANNEL_GROUP_SERVICE:
186 State wantedMileage = UnDefType.UNDEF;
187 if (!vehicleState.getRequiredServices().isEmpty()) {
188 if (vehicleState.getRequiredServices().get(0).getMileage() > 0) {
189 wantedMileage = QuantityType.valueOf(
190 vehicleState.getRequiredServices().get(0).getMileage(),
191 Constants.KILOMETRE_UNIT);
193 wantedMileage = UnDefType.UNDEF;
196 assertEquals(wantedMileage, state, "Service Mileage");
199 assertFalse(true, "Channel " + channelUID + " " + state + " not found");
204 assertTrue(isElectric, "Is Electric");
205 assertTrue(state instanceof QuantityType);
206 qt = ((QuantityType) state);
207 assertEquals(wantedUnit, qt.getUnit());
208 assertEquals(vehicleState.getElectricChargingState().getRange(), qt.intValue(), "Range Electric");
211 assertTrue(isHybrid, "Is hybrid");
212 assertTrue(state instanceof QuantityType);
213 qt = ((QuantityType) state);
214 assertEquals(wantedUnit, qt.getUnit());
215 assertEquals(vehicleState.getRange(), qt.intValue(), "Range combined hybrid");
218 assertTrue(hasFuel, "Has Fuel");
219 assertTrue(state instanceof QuantityType);
220 qt = ((QuantityType) state);
222 assertEquals(vehicleState.getCombustionFuelLevel().getRange(), qt.intValue(), "Range Combustion");
225 vehicleState.getCombustionFuelLevel().getRange()
226 - vehicleState.getElectricChargingState().getRange(),
227 qt.intValue(), "Range Combustion");
231 assertTrue(hasFuel, "Has Fuel");
232 assertTrue(state instanceof QuantityType);
233 qt = ((QuantityType) state);
234 assertEquals(Units.LITRE, qt.getUnit(), "Liter Unit");
235 assertEquals(vehicleState.getCombustionFuelLevel().getRemainingFuelLiters(), qt.intValue(),
238 case ESTIMATED_FUEL_L_100KM:
239 assertTrue(hasFuel, "Has Fuel");
241 if (vehicleState.getCombustionFuelLevel().getRemainingFuelLiters() > 0
242 && vehicleState.getCombustionFuelLevel().getRange() > 0) {
243 assertTrue(state instanceof DecimalType);
244 dt = ((DecimalType) state);
245 double estimatedFuelConsumptionL100km = vehicleState.getCombustionFuelLevel()
246 .getRemainingFuelLiters() * 1.0 / vehicleState.getCombustionFuelLevel().getRange() * 100.0;
247 assertEquals(estimatedFuelConsumptionL100km, dt.doubleValue(),
248 "Estimated Fuel Consumption l/100km");
250 assertTrue(state instanceof UnDefType);
253 case ESTIMATED_FUEL_MPG:
254 assertTrue(hasFuel, "Has Fuel");
256 if (vehicleState.getCombustionFuelLevel().getRemainingFuelLiters() > 0
257 && vehicleState.getCombustionFuelLevel().getRange() > 0) {
258 assertTrue(state instanceof DecimalType);
259 dt = ((DecimalType) state);
260 double estimatedFuelConsumptionMpg = 235.214583
261 / (vehicleState.getCombustionFuelLevel().getRemainingFuelLiters() * 1.0
262 / vehicleState.getCombustionFuelLevel().getRange() * 100.0);
263 assertEquals(estimatedFuelConsumptionMpg, dt.doubleValue(), "Estimated Fuel Consumption mpg");
265 assertTrue(state instanceof UnDefType);
269 assertTrue(isElectric, "Is Electric");
270 assertTrue(state instanceof QuantityType);
271 qt = ((QuantityType) state);
272 assertEquals(Units.PERCENT, qt.getUnit(), "Percent");
273 assertEquals(vehicleState.getElectricChargingState().getChargingLevelPercent(), qt.intValue(),
277 assertTrue(state instanceof StringType);
278 st = (StringType) state;
280 .valueOf(Converter.toTitleCase(vehicleState.getDoorsState().getCombinedSecurityState()));
281 assertEquals(wanted.toString(), st.toString(), "Vehicle locked");
284 assertTrue(state instanceof StringType);
285 st = (StringType) state;
286 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getDoorsState().getCombinedState()));
287 assertEquals(wanted.toString(), st.toString(), "Doors closed");
290 assertTrue(state instanceof StringType);
291 st = (StringType) state;
292 if (specialHandlingMap.containsKey(WINDOWS)) {
293 assertEquals(specialHandlingMap.get(WINDOWS).toString(), st.toString(), "Windows");
296 .valueOf(Converter.toTitleCase(vehicleState.getWindowsState().getCombinedState()));
297 assertEquals(wanted.toString(), st.toString(), "Windows");
302 assertTrue(state instanceof StringType);
303 st = (StringType) state;
304 if (specialHandlingMap.containsKey(CHECK_CONTROL)) {
305 assertEquals(specialHandlingMap.get(CHECK_CONTROL).toString(), st.toString(), "Check Control");
307 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getOverallCheckControlStatus()));
308 assertEquals(wanted.toString(), st.toString(), "Check Control");
311 case CHARGE_REMAINING:
312 // charge-remaining can be either a number in minutes, or UNDEF
313 assertTrue(isElectric, "Is Electric");
314 if (state instanceof QuantityType) {
315 assertTrue(state instanceof QuantityType);
316 qt = ((QuantityType) state);
317 assertEquals(Units.MINUTE, qt.getUnit(), "Minute Unit");
318 assertEquals(vehicleState.getElectricChargingState().getRemainingChargingMinutes(), qt.intValue(),
319 "Charge Time Remaining");
321 assertTrue(state instanceof UnDefType);
325 assertTrue(isElectric, "Is Electric");
326 assertTrue(state instanceof StringType);
327 st = (StringType) state;
328 assertEquals(Converter.toTitleCase(vehicleState.getElectricChargingState().getChargingStatus()),
329 st.toString(), "Charge Status");
331 case PLUG_CONNECTION:
332 assertTrue(state instanceof StringType);
333 st = (StringType) state;
334 assertEquals(Converter.getConnectionState(vehicleState.getElectricChargingState().isChargerConnected()),
335 st, "Plug Connection State");
338 assertTrue(state instanceof DateTimeType);
339 dtt = (DateTimeType) state;
340 State expectedUpdateDate = Converter.zonedToLocalDateTime(vehicleState.getLastUpdatedAt(),
341 ZoneId.systemDefault());
342 assertEquals(expectedUpdateDate.toString(), dtt.toString(), "Last Update");
345 assertTrue(state instanceof DateTimeType);
346 dtt = (DateTimeType) state;
347 State expectedFetchedDate = Converter.zonedToLocalDateTime(vehicleState.getLastFetched(),
348 ZoneId.systemDefault());
349 assertEquals(expectedFetchedDate.toString(), dtt.toString(), "Last Fetched");
352 if (state instanceof PointType) {
353 pt = (PointType) state;
354 assertNotNull(vehicleState.getLocation());
356 PointType.valueOf(Double.toString(vehicleState.getLocation().getCoordinates().getLatitude())
358 + Double.toString(vehicleState.getLocation().getCoordinates().getLongitude())),
360 } // else no check needed
363 if (state instanceof QuantityType quantityCommand) {
364 qt = quantityCommand;
365 assertEquals(Units.DEGREE_ANGLE, qt.getUnit(), "Angle Unit");
366 assertNotNull(vehicleState.getLocation());
367 assertEquals(vehicleState.getLocation().getHeading(), qt.intValue(), 0.01, "Heading");
368 } // else no check needed
370 case RANGE_RADIUS_ELECTRIC:
371 assertTrue(state instanceof QuantityType);
372 assertTrue(isElectric);
373 qt = ((QuantityType) state);
374 assertEquals(Converter.guessRangeRadius(vehicleState.getElectricChargingState().getRange()),
375 qt.intValue(), "Range Radius Electric");
377 case RANGE_RADIUS_FUEL:
378 assertTrue(state instanceof QuantityType);
380 qt = (QuantityType) state;
382 assertEquals(Converter.guessRangeRadius(vehicleState.getCombustionFuelLevel().getRange()),
383 qt.intValue(), "Range Radius Fuel");
386 Converter.guessRangeRadius(vehicleState.getCombustionFuelLevel().getRange()
387 - vehicleState.getElectricChargingState().getRange()),
388 qt.intValue(), "Range Radius Fuel");
391 case RANGE_RADIUS_HYBRID:
392 assertTrue(state instanceof QuantityType);
393 assertTrue(isHybrid);
394 qt = (QuantityType) state;
395 assertEquals(Converter.guessRangeRadius(vehicleState.getRange()), qt.intValue(), "Range Radius Hybrid");
397 case DOOR_DRIVER_FRONT:
398 assertTrue(state instanceof StringType);
399 st = (StringType) state;
400 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getDoorsState().getLeftFront()));
401 assertEquals(wanted.toString(), st.toString(), "Door");
403 case DOOR_DRIVER_REAR:
404 assertTrue(state instanceof StringType);
405 st = (StringType) state;
406 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getDoorsState().getLeftRear()));
407 assertEquals(wanted.toString(), st.toString(), "Door");
409 case DOOR_PASSENGER_FRONT:
410 assertTrue(state instanceof StringType);
411 st = (StringType) state;
412 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getDoorsState().getRightFront()));
413 assertEquals(wanted.toString(), st.toString(), "Door");
415 case DOOR_PASSENGER_REAR:
416 assertTrue(state instanceof StringType);
417 st = (StringType) state;
418 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getDoorsState().getRightRear()));
419 assertEquals(wanted.toString(), st.toString(), "Door");
422 assertTrue(state instanceof StringType);
423 st = (StringType) state;
424 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getDoorsState().getTrunk()));
425 assertEquals(wanted.toString(), st.toString(), "Door");
428 assertTrue(state instanceof StringType);
429 st = (StringType) state;
430 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getDoorsState().getHood()));
431 assertEquals(wanted.toString(), st.toString(), "Door");
433 case WINDOW_DOOR_DRIVER_FRONT:
434 assertTrue(state instanceof StringType);
435 st = (StringType) state;
436 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getWindowsState().getLeftFront()));
437 assertEquals(wanted.toString(), st.toString(), "Window");
439 case WINDOW_DOOR_DRIVER_REAR:
440 assertTrue(state instanceof StringType);
441 st = (StringType) state;
442 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getWindowsState().getLeftRear()));
443 assertEquals(wanted.toString(), st.toString(), "Window");
445 case WINDOW_DOOR_PASSENGER_FRONT:
446 assertTrue(state instanceof StringType);
447 st = (StringType) state;
448 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getWindowsState().getRightFront()));
449 assertEquals(wanted.toString(), st.toString(), "Window");
451 case WINDOW_DOOR_PASSENGER_REAR:
452 assertTrue(state instanceof StringType);
453 st = (StringType) state;
454 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getWindowsState().getRightRear()));
455 assertEquals(wanted.toString(), st.toString(), "Window");
458 assertTrue(state instanceof StringType);
459 st = (StringType) state;
460 wanted = StringType.valueOf(Converter.toTitleCase(vehicleState.getRoofState().getRoofState()));
461 assertEquals(wanted.toString(), st.toString(), "Window");
464 if (!state.equals(UnDefType.UNDEF)) {
465 assertTrue(state instanceof DateTimeType);
466 dtt = (DateTimeType) state;
467 if (gUid.contentEquals(CHANNEL_GROUP_STATUS)) {
468 if (specialHandlingMap.containsKey(SERVICE_DATE)) {
469 assertEquals(specialHandlingMap.get(SERVICE_DATE).toString(), dtt.toString(),
472 String dueDateString = VehicleStatusUtils
473 .getNextServiceDate(vehicleState.getRequiredServices()).toString();
474 DateTimeType expectedDTT = DateTimeType.valueOf(dueDateString);
475 assertEquals(expectedDTT.toString(), dtt.toString(), "Next Service");
477 } else if (gUid.equals(CHANNEL_GROUP_SERVICE)) {
478 String dueDateString = vehicleState.getRequiredServices().get(0).getDateTime();
479 State expectedDTT = Converter.zonedToLocalDateTime(dueDateString, ZoneId.systemDefault());
480 assertEquals(expectedDTT.toString(), dtt.toString(), "First Service Date");
484 case SERVICE_MILEAGE:
485 if (!state.equals(UnDefType.UNDEF)) {
486 qt = ((QuantityType) state);
487 if (gUid.contentEquals(CHANNEL_GROUP_STATUS)) {
488 QuantityType<Length> wantedQt = (QuantityType) VehicleStatusUtils
489 .getNextServiceMileage(vehicleState.getRequiredServices());
490 assertEquals(wantedQt.getUnit(), qt.getUnit(), "Next Service Miles");
491 assertEquals(wantedQt.intValue(), qt.intValue(), "Mileage");
492 } else if (gUid.equals(CHANNEL_GROUP_SERVICE)) {
493 assertEquals(vehicleState.getRequiredServices().get(0).getMileage(), qt.intValue(),
494 "First Service Mileage");
499 assertTrue(state instanceof StringType);
500 st = (StringType) state;
502 case CHANNEL_GROUP_SERVICE:
503 wanted = StringType.valueOf(Constants.NO_ENTRIES);
504 if (!vehicleState.getRequiredServices().isEmpty()) {
505 wanted = StringType.valueOf(
506 Converter.toTitleCase(vehicleState.getRequiredServices().get(0).getType()));
508 assertEquals(wanted.toString(), st.toString(), "Service Name");
510 case CHANNEL_GROUP_CHECK_CONTROL:
511 wanted = StringType.valueOf(Constants.NO_ENTRIES);
512 if (!vehicleState.getCheckControlMessages().isEmpty()) {
513 wanted = StringType.valueOf(
514 Converter.toTitleCase(vehicleState.getCheckControlMessages().get(0).getType()));
516 assertEquals(wanted.toString(), st.toString(), "CheckControl Name");
519 assertFalse(true, "Channel " + channelUID + " " + state + " not found");
524 assertTrue(state instanceof StringType);
525 st = (StringType) state;
527 case CHANNEL_GROUP_SERVICE:
528 wanted = StringType.valueOf(Converter.toTitleCase(Constants.NO_ENTRIES));
529 if (!vehicleState.getRequiredServices().isEmpty()) {
530 wanted = StringType.valueOf(vehicleState.getRequiredServices().get(0).getDescription());
532 assertEquals(wanted.toString(), st.toString(), "Service Details");
534 case CHANNEL_GROUP_CHECK_CONTROL:
535 wanted = StringType.valueOf(Constants.NO_ENTRIES);
536 if (!vehicleState.getCheckControlMessages().isEmpty()) {
537 wanted = StringType.valueOf(vehicleState.getCheckControlMessages().get(0).getDescription());
539 assertEquals(wanted.toString(), st.toString(), "CheckControl Details");
542 assertFalse(true, "Channel " + channelUID + " " + state + " not found");
547 assertTrue(state instanceof StringType);
548 st = (StringType) state;
549 wanted = StringType.valueOf(Constants.NO_ENTRIES);
550 if (!vehicleState.getCheckControlMessages().isEmpty()) {
551 wanted = StringType.valueOf(
552 Converter.toTitleCase(vehicleState.getCheckControlMessages().get(0).getSeverity()));
554 assertEquals(wanted.toString(), st.toString(), "CheckControl Severity");
557 if (state.equals(UnDefType.UNDEF)) {
558 for (RequiredService serviceEntry : vehicleState.getRequiredServices()) {
559 assertTrue(serviceEntry.getDateTime() == null, "No Service Date available");
562 assertTrue(state instanceof DateTimeType);
563 dtt = (DateTimeType) state;
565 case CHANNEL_GROUP_SERVICE:
566 String dueDateString = vehicleState.getRequiredServices().get(0).getDateTime();
567 State expectedDTT = Converter.zonedToLocalDateTime(dueDateString, ZoneId.systemDefault());
568 assertEquals(expectedDTT.toString(), dtt.toString(), "ServiceSate");
571 assertFalse(true, "Channel " + channelUID + " " + state + " not found");
576 case FRONT_LEFT_CURRENT:
577 if (vehicleState.getTireState().getFrontLeft().getStatus().getCurrentPressure() > 0) {
578 assertTrue(state instanceof QuantityType);
579 qt = (QuantityType) state;
580 assertEquals(vehicleState.getTireState().getFrontLeft().getStatus().getCurrentPressure() / 100.0,
581 qt.doubleValue(), "Fron Left Current");
583 assertEquals(state, UnDefType.UNDEF);
586 case FRONT_LEFT_TARGET:
587 if (vehicleState.getTireState().getFrontLeft().getStatus().getTargetPressure() > 0) {
588 assertTrue(state instanceof QuantityType);
589 qt = (QuantityType) state;
590 assertEquals(vehicleState.getTireState().getFrontLeft().getStatus().getTargetPressure() / 100.0,
591 qt.doubleValue(), "Fron Left Current");
593 assertTrue(state.equals(UnDefType.UNDEF));
596 case FRONT_RIGHT_CURRENT:
597 if (vehicleState.getTireState().getFrontRight().getStatus().getCurrentPressure() > 0) {
598 assertTrue(state instanceof QuantityType);
599 qt = (QuantityType) state;
600 assertEquals(vehicleState.getTireState().getFrontRight().getStatus().getCurrentPressure() / 100.0,
601 qt.doubleValue(), "Fron Left Current");
603 assertTrue(state.equals(UnDefType.UNDEF));
606 case FRONT_RIGHT_TARGET:
607 if (vehicleState.getTireState().getFrontRight().getStatus().getTargetPressure() > 0) {
608 assertTrue(state instanceof QuantityType);
609 qt = (QuantityType) state;
610 assertEquals(vehicleState.getTireState().getFrontRight().getStatus().getTargetPressure() / 100.0,
611 qt.doubleValue(), "Fron Left Current");
613 assertTrue(state.equals(UnDefType.UNDEF));
616 case REAR_LEFT_CURRENT:
617 if (vehicleState.getTireState().getRearLeft().getStatus().getCurrentPressure() > 0) {
618 assertTrue(state instanceof QuantityType);
619 qt = (QuantityType) state;
620 assertEquals(vehicleState.getTireState().getRearLeft().getStatus().getCurrentPressure() / 100.0,
621 qt.doubleValue(), "Fron Left Current");
623 assertTrue(state.equals(UnDefType.UNDEF));
626 case REAR_LEFT_TARGET:
627 if (vehicleState.getTireState().getRearLeft().getStatus().getTargetPressure() > 0) {
628 assertTrue(state instanceof QuantityType);
629 qt = (QuantityType) state;
630 assertEquals(vehicleState.getTireState().getRearLeft().getStatus().getTargetPressure() / 100.0,
631 qt.doubleValue(), "Fron Left Current");
633 assertTrue(state.equals(UnDefType.UNDEF));
636 case REAR_RIGHT_CURRENT:
637 if (vehicleState.getTireState().getRearRight().getStatus().getCurrentPressure() > 0) {
638 assertTrue(state instanceof QuantityType);
639 qt = (QuantityType) state;
640 assertEquals(vehicleState.getTireState().getRearRight().getStatus().getCurrentPressure() / 100.0,
641 qt.doubleValue(), "Fron Left Current");
643 assertTrue(state.equals(UnDefType.UNDEF));
646 case REAR_RIGHT_TARGET:
647 if (vehicleState.getTireState().getRearRight().getStatus().getTargetPressure() > 0) {
648 assertTrue(state instanceof QuantityType);
649 qt = (QuantityType) state;
650 assertEquals(vehicleState.getTireState().getRearRight().getStatus().getTargetPressure() / 100.0,
651 qt.doubleValue(), "Fron Left Current");
653 assertTrue(state.equals(UnDefType.UNDEF));
657 if (state instanceof StringType) {
658 st = (StringType) state;
659 assertEquals(st.toFullString(), vehicleState.getLocation().getAddress().getFormatted(),
661 } // else no check needed
664 if (state instanceof QuantityType quantity) {
666 PointType vehicleLocation = PointType
667 .valueOf(Double.toString(vehicleState.getLocation().getCoordinates().getLatitude()) + ","
668 + Double.toString(vehicleState.getLocation().getCoordinates().getLongitude()));
669 int distance = vehicleLocation.distanceFrom(VehicleHandlerTest.HOME_LOCATION).intValue();
670 assertEquals(qt.intValue(), distance, "Distance from Home");
671 assertEquals(qt.getUnit(), SIUnits.METRE, "Distance from Home Unit");
672 } // else no check needed
675 // don't assert raw channel
678 if (!gUid.equals(CHANNEL_GROUP_CHARGE_PROFILE)) {
679 // fail in case of unknown update
680 assertFalse(true, "Channel " + channelUID + " " + state + " not found");