]> git.basschouten.com Git - openhab-addons.git/blob
8b7cc0a08f8fb63346bd77eac303a36bd544f9f8
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mybmw.internal.utils;
14
15 import java.time.ZonedDateTime;
16 import java.util.List;
17
18 import javax.measure.Unit;
19 import javax.measure.quantity.Length;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.mybmw.internal.MyBMWConstants.VehicleType;
24 import org.openhab.binding.mybmw.internal.dto.properties.CBS;
25 import org.openhab.binding.mybmw.internal.dto.status.FuelIndicator;
26 import org.openhab.binding.mybmw.internal.dto.vehicle.Vehicle;
27 import org.openhab.core.library.types.DateTimeType;
28 import org.openhab.core.library.types.QuantityType;
29 import org.openhab.core.library.unit.ImperialUnits;
30 import org.openhab.core.types.State;
31 import org.openhab.core.types.UnDefType;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * The {@link VehicleStatusUtils} Data Transfer Object
37  *
38  * @author Bernd Weymann - Initial contribution
39  */
40 @NonNullByDefault
41 public class VehicleStatusUtils {
42     public static final Logger LOGGER = LoggerFactory.getLogger(VehicleStatusUtils.class);
43
44     public static State getNextServiceDate(List<CBS> cbsMessageList) {
45         ZonedDateTime farFuture = ZonedDateTime.now().plusYears(100);
46         ZonedDateTime serviceDate = farFuture;
47         for (CBS service : cbsMessageList) {
48             if (service.dateTime != null) {
49                 ZonedDateTime d = ZonedDateTime.parse(service.dateTime);
50                 if (d.isBefore(serviceDate)) {
51                     serviceDate = d;
52                 } // else skip
53             }
54         }
55         if (serviceDate.equals(farFuture)) {
56             return UnDefType.UNDEF;
57         } else {
58             return DateTimeType.valueOf(serviceDate.format(Converter.DATE_INPUT_PATTERN));
59         }
60     }
61
62     public static State getNextServiceMileage(List<CBS> cbsMessageList) {
63         boolean imperial = false;
64         int serviceMileage = Integer.MAX_VALUE;
65         for (CBS service : cbsMessageList) {
66             if (service.distance != null) {
67                 if (service.distance.value < serviceMileage) {
68                     serviceMileage = service.distance.value;
69                     imperial = !Constants.KILOMETERS_JSON.equals(service.distance.units);
70                 }
71             }
72         }
73         if (serviceMileage != Integer.MAX_VALUE) {
74             if (imperial) {
75                 return QuantityType.valueOf(serviceMileage, ImperialUnits.MILE);
76             } else {
77                 return QuantityType.valueOf(serviceMileage, Constants.KILOMETRE_UNIT);
78             }
79         } else {
80             return UnDefType.UNDEF;
81         }
82     }
83
84     /**
85      * calculates the mapping of thing type
86      *
87      * @param driveTrain
88      * @param model
89      * @return
90      */
91     public static VehicleType vehicleType(String driveTrain, String model) {
92         if (Constants.BEV.equals(driveTrain)) {
93             if (model.endsWith(Constants.REX_EXTENSION)) {
94                 return VehicleType.ELECTRIC_REX;
95             } else {
96                 return VehicleType.ELECTRIC;
97             }
98         } else if (Constants.PHEV.equals(driveTrain)) {
99             return VehicleType.PLUGIN_HYBRID;
100         } else if (Constants.CONV.equals(driveTrain) || Constants.HYBRID.equals(driveTrain)) {
101             return VehicleType.CONVENTIONAL;
102         }
103         LOGGER.warn("Unknown Vehicle Type: {} | {}", model, driveTrain);
104         return VehicleType.UNKNOWN;
105     }
106
107     public static @Nullable Unit<Length> getLengthUnit(List<FuelIndicator> indicators) {
108         Unit<Length> ret = null;
109         for (FuelIndicator fuelIndicator : indicators) {
110             String unitAbbrev = fuelIndicator.rangeUnits;
111             switch (unitAbbrev) {
112                 case Constants.KM_JSON:
113                     if (ret != null) {
114                         if (!ret.equals(Constants.KILOMETRE_UNIT)) {
115                             LOGGER.debug("Ambigious Unit declarations. Found {} before {}", ret, Constants.KM_JSON);
116                         } // else - fine!
117                     } else {
118                         ret = Constants.KILOMETRE_UNIT;
119                     }
120                     break;
121                 case Constants.MI_JSON:
122                     if (ret != null) {
123                         if (!ret.equals(ImperialUnits.MILE)) {
124                             LOGGER.debug("Ambigious Unit declarations. Found {} before {}", ret, Constants.MI_JSON);
125                         } // else - fine!
126                     } else {
127                         ret = ImperialUnits.MILE;
128                     }
129                     break;
130                 default:
131                     LOGGER.debug("Cannot evaluate Unit for {}", unitAbbrev);
132                     break;
133             }
134         }
135         return ret;
136     }
137
138     /**
139      * The range values delivered by BMW are quite ambiguous!
140      * - status fuel indicators are missing a unique identifier
141      * - properties ranges delivering wrong values for hybrid and fuel range
142      * - properties ranges are not reflecting mi / km - every time km
143      *
144      * So getRange will try
145      * 1) fuel indicator
146      * 2) ranges from properties, except combined range
147      * 3) take a guess from fuel indicators
148      *
149      * @param unitJson
150      * @param vehicle
151      * @return
152      */
153     public static int getRange(String unitJson, Vehicle vehicle) {
154         if (vehicle.status.fuelIndicators.size() == 1) {
155             return Converter.stringToInt(vehicle.status.fuelIndicators.get(0).rangeValue);
156         } else {
157             return guessRange(unitJson, vehicle);
158         }
159     }
160
161     /**
162      * Guesses the range from 3 fuelindicators
163      * - electric range calculation is correct
164      * - for the 2 other values:
165      * -- smaller one is assigned to fuel range
166      * -- bigger one is assigned to hybrid range
167      *
168      * @see org.openhab.binding.mybmw.internal.dto.VehicleStatusTest testGuessRange
169      *
170      * @param unitJson
171      * @param vehicle
172      * @return
173      */
174     public static int guessRange(String unitJson, Vehicle vehicle) {
175         int electricGuess = Constants.INT_UNDEF;
176         int fuelGuess = Constants.INT_UNDEF;
177         int hybridGuess = Constants.INT_UNDEF;
178         for (FuelIndicator fuelIndicator : vehicle.status.fuelIndicators) {
179             // electric range - this fits 100%
180             if (Constants.UNIT_PRECENT_JSON.equals(fuelIndicator.levelUnits)
181                     && fuelIndicator.chargingStatusType != null) {
182                 // found electric
183                 electricGuess = Converter.stringToInt(fuelIndicator.rangeValue);
184             } else {
185                 if (fuelGuess == Constants.INT_UNDEF) {
186                     // fuel not set? then assume it's fuel
187                     fuelGuess = Converter.stringToInt(fuelIndicator.rangeValue);
188                 } else {
189                     // fuel already guessed - take smaller value for fuel, bigger for hybrid
190                     int newGuess = Converter.stringToInt(fuelIndicator.rangeValue);
191                     hybridGuess = Math.max(fuelGuess, newGuess);
192                     fuelGuess = Math.min(fuelGuess, newGuess);
193                 }
194             }
195         }
196         switch (unitJson) {
197             case Constants.UNIT_PRECENT_JSON:
198                 return electricGuess;
199             case Constants.UNIT_LITER_JSON:
200                 return fuelGuess;
201             case Constants.PHEV:
202                 return hybridGuess;
203             default:
204                 return Constants.INT_UNDEF;
205         }
206     }
207
208     public static String getChargStatus(Vehicle vehicle) {
209         FuelIndicator fi = getElectricFuelIndicator(vehicle);
210         if (fi.chargingStatusType != null) {
211             if (fi.chargingStatusType.equals(Constants.DEFAULT)) {
212                 return Constants.NOT_CHARGING_STATE;
213             } else {
214                 return fi.chargingStatusType;
215             }
216         }
217         return Constants.UNDEF;
218     }
219
220     public static String getChargeInfo(Vehicle vehicle) {
221         FuelIndicator fi = getElectricFuelIndicator(vehicle);
222         if (fi.chargingStatusType != null && fi.infoLabel != null) {
223             if (fi.chargingStatusType.equals(Constants.CHARGING_STATE)
224                     || fi.chargingStatusType.equals(Constants.PLUGGED_STATE)) {
225                 return fi.infoLabel;
226             }
227         }
228         return Constants.HYPHEN;
229     }
230
231     private static FuelIndicator getElectricFuelIndicator(Vehicle vehicle) {
232         for (FuelIndicator fuelIndicator : vehicle.status.fuelIndicators) {
233             if (Constants.UNIT_PRECENT_JSON.equals(fuelIndicator.levelUnits)
234                     && fuelIndicator.chargingStatusType != null) {
235                 return fuelIndicator;
236             }
237         }
238         return new FuelIndicator();
239     }
240 }