]> git.basschouten.com Git - openhab-addons.git/blob
0b2d862ea44efbc3b9740e3831696232eb2932b8
[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             DateTimeType dt = DateTimeType.valueOf(serviceDate.format(Converter.DATE_INPUT_PATTERN));
59             return dt;
60         }
61     }
62
63     public static State getNextServiceMileage(List<CBS> cbsMessageList) {
64         boolean imperial = false;
65         int serviceMileage = Integer.MAX_VALUE;
66         for (CBS service : cbsMessageList) {
67             if (service.distance != null) {
68                 if (service.distance.value < serviceMileage) {
69                     serviceMileage = service.distance.value;
70                     imperial = !Constants.KILOMETERS_JSON.equals(service.distance.units);
71                 }
72             }
73         }
74         if (serviceMileage != Integer.MAX_VALUE) {
75             if (imperial) {
76                 return QuantityType.valueOf(serviceMileage, ImperialUnits.MILE);
77             } else {
78                 return QuantityType.valueOf(serviceMileage, Constants.KILOMETRE_UNIT);
79             }
80         } else {
81             return UnDefType.UNDEF;
82         }
83     }
84
85     /**
86      * calculates the mapping of thing type
87      *
88      * @param driveTrain
89      * @param model
90      * @return
91      */
92     public static VehicleType vehicleType(String driveTrain, String model) {
93         if (Constants.BEV.equals(driveTrain)) {
94             if (model.endsWith(Constants.REX_EXTENSION)) {
95                 return VehicleType.ELECTRIC_REX;
96             } else {
97                 return VehicleType.ELECTRIC;
98             }
99         } else if (Constants.PHEV.equals(driveTrain)) {
100             return VehicleType.PLUGIN_HYBRID;
101         } else if (Constants.CONV.equals(driveTrain) || Constants.HYBRID.equals(driveTrain)) {
102             return VehicleType.CONVENTIONAL;
103         }
104         LOGGER.warn("Unknown Vehicle Type: {} | {}", model, driveTrain);
105         return VehicleType.UNKNOWN;
106     }
107
108     public static @Nullable Unit<Length> getLengthUnit(List<FuelIndicator> indicators) {
109         Unit<Length> ret = null;
110         for (FuelIndicator fuelIndicator : indicators) {
111             String unitAbbrev = fuelIndicator.rangeUnits;
112             switch (unitAbbrev) {
113                 case Constants.KM_JSON:
114                     if (ret != null) {
115                         if (!ret.equals(Constants.KILOMETRE_UNIT)) {
116                             LOGGER.debug("Ambigious Unit declarations. Found {} before {}", ret, Constants.KM_JSON);
117                         } // else - fine!
118                     } else {
119                         ret = Constants.KILOMETRE_UNIT;
120                     }
121                     break;
122                 case Constants.MI_JSON:
123                     if (ret != null) {
124                         if (!ret.equals(ImperialUnits.MILE)) {
125                             LOGGER.debug("Ambigious Unit declarations. Found {} before {}", ret, Constants.MI_JSON);
126                         } // else - fine!
127                     } else {
128                         ret = ImperialUnits.MILE;
129                     }
130                     break;
131                 default:
132                     LOGGER.debug("Cannot evaluate Unit for {}", unitAbbrev);
133                     break;
134             }
135         }
136         return ret;
137     }
138
139     /**
140      * The range values delivered by BMW are quite ambiguous!
141      * - status fuel indicators are missing a unique identifier
142      * - properties ranges delivering wrong values for hybrid and fuel range
143      * - properties ranges are not reflecting mi / km - every time km
144      *
145      * So getRange will try
146      * 1) fuel indicator
147      * 2) ranges from properties, except combined range
148      * 3) take a guess from fuel indicators
149      *
150      * @param unitJson
151      * @param indicators
152      * @return
153      */
154     public static int getRange(String unitJson, Vehicle vehicle) {
155         if (vehicle.status.fuelIndicators.size() == 1) {
156             return Converter.stringToInt(vehicle.status.fuelIndicators.get(0).rangeValue);
157         } else {
158             return guessRange(unitJson, vehicle);
159         }
160     }
161
162     /**
163      * Guesses the range from 3 fuelindicators
164      * - electric range calculation is correct
165      * - for the 2 other values:
166      * -- smaller one is assigned to fuel range
167      * -- bigger one is assigned to hybrid range
168      *
169      * @see VehicleStatusTest testGuessRange
170      *
171      * @param unitJson
172      * @param vehicle
173      * @return
174      */
175     public static int guessRange(String unitJson, Vehicle vehicle) {
176         int electricGuess = Constants.INT_UNDEF;
177         int fuelGuess = Constants.INT_UNDEF;
178         int hybridGuess = Constants.INT_UNDEF;
179         for (FuelIndicator fuelIndicator : vehicle.status.fuelIndicators) {
180             // electric range - this fits 100%
181             if (Constants.UNIT_PRECENT_JSON.equals(fuelIndicator.levelUnits)
182                     && fuelIndicator.chargingStatusType != null) {
183                 // found electric
184                 electricGuess = Converter.stringToInt(fuelIndicator.rangeValue);
185             } else {
186                 if (fuelGuess == Constants.INT_UNDEF) {
187                     // fuel not set? then assume it's fuel
188                     fuelGuess = Converter.stringToInt(fuelIndicator.rangeValue);
189                 } else {
190                     // fuel already guessed - take smaller value for fuel, bigger for hybrid
191                     int newGuess = Converter.stringToInt(fuelIndicator.rangeValue);
192                     hybridGuess = Math.max(fuelGuess, newGuess);
193                     fuelGuess = Math.min(fuelGuess, newGuess);
194                 }
195             }
196         }
197         switch (unitJson) {
198             case Constants.UNIT_PRECENT_JSON:
199                 return electricGuess;
200             case Constants.UNIT_LITER_JSON:
201                 return fuelGuess;
202             case Constants.PHEV:
203                 return hybridGuess;
204             default:
205                 return Constants.INT_UNDEF;
206         }
207     }
208
209     public static String getChargStatus(Vehicle vehicle) {
210         FuelIndicator fi = getElectricFuelIndicator(vehicle);
211         if (fi.chargingStatusType != null) {
212             if (fi.chargingStatusType.equals(Constants.DEFAULT)) {
213                 return Constants.NOT_CHARGING_STATE;
214             } else {
215                 return fi.chargingStatusType;
216             }
217         }
218         return Constants.UNDEF;
219     }
220
221     public static String getChargeInfo(Vehicle vehicle) {
222         FuelIndicator fi = getElectricFuelIndicator(vehicle);
223         if (fi.chargingStatusType != null && fi.infoLabel != null) {
224             if (fi.chargingStatusType.equals(Constants.CHARGING_STATE)
225                     || fi.chargingStatusType.equals(Constants.PLUGGED_STATE)) {
226                 return fi.infoLabel;
227             }
228         }
229         return Constants.HYPHEN;
230     }
231
232     private static FuelIndicator getElectricFuelIndicator(Vehicle vehicle) {
233         for (FuelIndicator fuelIndicator : vehicle.status.fuelIndicators) {
234             if (Constants.UNIT_PRECENT_JSON.equals(fuelIndicator.levelUnits)
235                     && fuelIndicator.chargingStatusType != null) {
236                 return fuelIndicator;
237             }
238         }
239         return new FuelIndicator();
240     }
241 }