[MercedesMe] Switch to Mercedes App SDK (#15628)

* add protocol buffer definitions
* oauth rework
* websocket introduction

Signed-off-by: Bernd Weymann <bernd.weymann@gmail.com>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
Bernd Weymann 2024-06-04 21:27:41 +02:00 committed by Ciprian Pascu
parent 450d514952
commit 1789d545cd
152 changed files with 156445 additions and 2789 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -10,6 +10,28 @@
<version>4.2.0-SNAPSHOT</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>add-source</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<sources>
<source>src/3rdparty/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- version needs to match with other projects like org.openhab.io.openhabcloud.pom.xml -->
<dependency>
@ -18,6 +40,13 @@
<version>20231013</version>
<scope>compile</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>4.26.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
<artifactId>org.openhab.binding.mercedesme</artifactId>

View File

@ -0,0 +1,7 @@
# How to protoc
- Check [mvn repository](https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util) which version to use
- Download correct protoc compiler from [maven central](https://repo1.maven.org/maven2/com/google/protobuf/protoc/) into `PROTOC_DIR`
- Call in mercedesme binding directory `PROTOC_DIR\protoc -I=proto --java_out=gen proto/*.proto`
- Move generated sources including subdirs from `gen` to `3rdparty\java`
- Adapt `pom.xml` with version of step 1

View File

@ -0,0 +1,420 @@
syntax = "proto3";
package proto;
//import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "gogo.proto";
option (gogoproto.goproto_enum_prefix_all) = true;
option java_package = "com.daimler.mbcarkit.proto";
message VVA {
enum CommandState {
UNKNOWN_COMMAND_STATE = 0;
CREATED = 1010;
ENQUEUED = 1016;
PROCESSING = 1012;
SUSPENDED = 1017;
FINISHED = 1018;
}
enum CommandCondition {
UNKNWON_COMMAND_CONDITION = 0;
NONE = 1000;
ACCEPTED = 1001;
REJECTED = 1002;
TERMINATE = 1003;
SUCCESS = 1011;
FAILED = 1013;
OVERWRITTEN = 1014;
TIMEOUT = 1015;
}
}
message VehicleAPI {
enum CommandState {
UNKNOWN_COMMAND_STATE = 0;
// Command execution request is accepted and an asynchronous process is
// being initialized.
INITIATION = 1;
// Another process for the same vehicle and queue is active, the request has
// been queued for later execution.
ENQUEUED = 2;
// The process is currently being processed by the backend.
PROCESSING = 3;
// The backend currently waits for the vehicle to respond to the request.
WAITING = 4;
// The process has finished successfully.
FINISHED = 5;
// There was an error while executing the command process.
FAILED = 6;
}
enum AttributeStatus {
// Value is set and valid
VALUE_SET = 0;
// Value has not yet been retrieved from vehicle (but sensor etc. should be available)
VALUE_NOT_SET = 1;
// Value has been retrieved from vehicle but is invalid (marked as invalid by DaiVB backend)
INVALID = 3;
// Vehicle does not support this attribute (e.g. does not have the sensor etc.)
NOT_AVAILABLE = 4;
}
enum QueueType {
// the JSON enum values are lowercase, but lowercase values are not exported in golang, so
// we allow aliasing all values.
option allow_alias = true;
UNKNOWNCOMMANDQUEUETYPE = 0;
DOORS = 10;
AUXHEAT = 11;
PRECOND = 12;
CHARGEOPT = 13;
MAINTENANCE = 14;
TCU = 15;
FEED = 16;
SERVICEACTIVATION = 17;
ATP = 18;
ASSISTANCE = 19;
RACP = 20;
WEEKPROFILE = 21;
REMOTEDIAGNOSIS = 22;
FLSH = 23; //(ALSO USED BY SIGPOS/RVF)
TEMPERATURE = 24;
TRIPCOMP = 25;
ENGINE = 26;
THEFTALARM = 27;
WINDOW = 28;
HEADUNIT = 29;
MECALL = 31;
IMMOBILIZER = 32;
RENTALSIGNAL = 33;
BCF = 34; //(BLACKCHANNEL)
PLUGANDCHARGE = 35;
CARSHARINGMODULE = 36;
BATTERY = 37;
ONBOARDFENCES = 38;
SPEEDFENCES = 39;
CHARGINGTARIFFS = 40;
RTMCONFIG = 41;
MAINTENANCECOMPUTER = 42;
MECALL2 = 43;
AUTOMATEDVALETPARKING = 44;
CHARGECONTROL = 45;
SPEEDALERT = 46;
unknowncommandqueuetype = 0;
doors = 10;
auxheat = 11;
precond = 12;
chargeopt = 13;
maintenance = 14;
tcu = 15;
feed = 16;
serviceactivation = 17;
atp = 18;
assistance = 19;
racp = 20;
weekprofile = 21;
remotediagnosis = 22;
flsh = 23; //(also used by sigpos/RVF)
temperature = 24;
tripcomp = 25;
engine = 26;
theftalarm = 27;
window = 28;
headunit = 29;
mecall = 31;
immobilizer = 32;
rentalsignal = 33;
bcf = 34; //(blackchannel)
plugandcharge = 35;
carsharingmodule = 36;
battery = 37;
onboardfences = 38;
speedfences = 39;
chargingtariffs = 40;
rtmconfig = 41;
maintenancecomputer = 42;
mecall2 = 43;
automatedvaletparking = 44;
chargecontrol = 45;
speedalert = 46;
}
}
message ACP {
enum CommandType {
// the JSON enum values are lowercase, but lowercase values are not exported in golang, so
// we allow aliasing all values.
option allow_alias = true;
UNKNOWNCOMMANDTYPE = 0;
DOORSLOCK = 100;
DOORSUNLOCK = 110;
TRUNKUNLOCK = 115;
FUELFLAPUNLOCK = 116;
CHARGEFLAPUNLOCK = 117;
CHARGECOUPLERUNLOCK = 118;
DOORSPREPARERENTAL = 120;
DOORSSECUREVEHICLE = 130;
AUXHEATSTART = 300;
AUXHEATSTOP = 310;
AUXHEATCONFIGURE = 320;
TEMPERATURECONFIGURE = 350;
WEEKPROFILECONFIGURE = 360;
WEEKPROFILEV2CONFIGURE = 370;
PRECONDSTART = 400;
PRECONDSTOP = 410;
PRECONDCONFIGURE = 420;
PRECONDCONFIGURESEATS = 425;
CHARGEOPTCONFIGURE = 430;
CHARGEOPTSTART = 440;
CHARGEOPTSTOP = 450;
FEEDPOI = 500;
FEEDFREETEXT = 510;
ENGINESTART = 550;
ENGINESTOP = 560;
ENGINEAVPSTART = 570;
TCUWAKEUP = 600;
TCUSWUPDATE = 610;
TCURCSRESET = 620;
TCUINTERROGATION = 630;
SPEEDALERTSTART = 710;
SPEEDALERTSTOP = 720;
FLSHSTART = 750; // (DEPRECATED)
FLSHSTOP = 760; // (DEPRECATED)
SIGPOSSTART = 770;
CONTRACTCONFIGURE = 800;
CONTRACTREMOVE = 810;
ROOTCONFIGURE = 820;
ROOTREMOVE = 830;
TRIPCOMP = 850; // RESET TRIPCOMP
MAINTENANCECONFIGURE = 930;
MAINTENANCECOMPUTEROFFSET = 931;
SHORTTESTEXECUTE = 935;
SERVICEACTIVATIONCONFIGURE = 940;
DC2SERVICEACTIVATIONCONFIGURE = 945;
DC2RAWDOWNLOAD = 950; //(TEST COMMAND)
APPLICATIONCONFIGURATION = 955; // (DC2+)
DC2STARTTRACKING = 960; // (TEST COMMAND)
ATPSEQUENCE = 990;
THEFTALARMTOGGLEINTERIOR = 1000;
THEFTALARMTOGGLETOW = 1010;
THEFTALARMSELECTINTERIORTOW = 1020;
THEFTALARMDESELECTINTERIORTOW = 1030;
THEFTALARMSTOP = 1040;
WINDOWOPEN = 1100;
WINDOWCLOSE = 1110;
WINDOWVENTILATE = 1120;
WINDOWMOVE = 1121;
ROOFOPEN = 1130;
ROOFCLOSE = 1140;
ROOFLIFT = 1150;
ROOFMOVE = 1151;
BATTERYMAXSOC = 2000;
BATTERYCHARGEPROGRAM = 2010;
CHARGEPROGRAMCONFIGURE = 2020;
ONBOARDFENCESCREATE = 2100;
ONBOARDFENCESUPDATE = 2110;
ONBOARDFENCESDELETE = 2120;
SPEEDFENCESCREATE = 2200;
SPEEDFENCESUPDATE = 2210;
SPEEDFENCESDELETE = 2220;
CHARGINGTARIFFSCREATE = 2300;
CHARGINGTARIFFSUPDATE = 2310;
CHARGINGTARIFFSDELETE = 2320;
THEFTALARMSTART = 2500;
THEFTALARMSELECTINTERIOR = 2510;
THEFTALARMDESELECTINTERIOR = 2520;
THEFTALARMSELECTTOW = 2530;
THEFTALARMDESELECTTOW = 2540;
THEFTALARMSELECTDAMAGEDETECTION = 2550;
THEFTALARMDESELECTDAMAGEDETECTION = 2560;
THEFTALARMCONFIRMDAMAGEDETECTION = 2570;
MECALL2START = 2600;
UDXTRIGGERSYNCHRONIZATION = 1200;
UDXACTIVEUSERPROFILE = 1210;
UDXRESETUSERDATA = 1220;
USERPROFSYNCH = 1230;
USERDATARESET = 1240;
PROFACTIVATIONSNAP = 1250;
PROFACTIVATIONDIRECT = 1255;
SOFTWAREUPDATE = 1260;
PUSHNOTIFICATION = 1270;
MECALLCOMMAND = 1310;
PRECONDSTARTRCS = 1400;
PRECONDSTOPRCS = 1410;
PRECONDCONFIGURERCS = 1420;
TCUCONFIGURE = 1430;
EDISONSERVICEACTIVATION = 1431;
TESTSEQUENCE = 1432;
PRECONDCONFIGURERACP = 1433;
CHARGEOPTCONFIGURERACP = 1434;
TARIFFTABLEDOWNLOAD = 1435;
PRECONDSTARTRACP = 1436;
PRECONDSTOPRACP = 1437;
ROOTCERTIFICATEREMOVE = 1438;
ONREQUESTPROBEUPLOAD = 1439;
ROOTCERTIFICATEDOWNLOAD = 1440;
CONTRACTCERTIFICATEREMOVE = 1441;
CONTRACTCERTIFICATEDOWNLOAD = 1442;
PROBECONFIGURATIONUPDATE = 1443;
RDIAGDELETEECU = 1500;
RDIAGSTATUSREPORT = 1501;
RDIAGEXECUTION = 1502;
IMMOBILIZERCHALLENGE = 1600;
IMMOBILIZERSEARCHKEYLINE = 1610;
IMMOBILIZERRELEASEKEYLINE = 1620;
IMMOBILIZERLOCKKEYLINE = 1630;
IMMOBILIZERLOCKVEHICLE = 1631;
IMMOBILIZERRELEASEVEHICLE = 1621;
SETRENTALSIGNAL = 1700;
BLACKCHANNELDOWNLOAD = 1800;
BLACKCHANNELUPLOAD = 1810;
CONFIGURECSM = 1900;
UPDATEVEHICLEINFO = 1901;
RELAYMESSAGETOCSM = 1902;
RELAYRENTALREQUESTTOCSB = 1903;
RTMDOWNLOADCONFIG = 2400;
RTMREADCONFIG = 2410;
AVPACTIVATE = 2700;
CHARGECONTROLCONFIGURE = 2800;
unknownCommandType = 0;
doorsLock = 100;
doorsUnlock = 110;
trunkUnlock = 115;
fuelflapUnlock = 116;
chargeflapUnlock = 117;
chargecouplerUnlock = 118;
doorsPrepareRental = 120;
doorsSecureVehicle = 130;
auxheatStart = 300;
auxheatStop = 310;
auxheatConfigure = 320;
temperatureConfigure = 350;
weekprofileConfigure = 360;
weekprofileV2Configure = 370;
precondStart = 400;
precondStop = 410;
precondConfigure = 420;
precondConfigureSeats = 425;
chargeoptConfigure = 430;
chargeoptStart = 440;
chargeoptStop = 450;
feedPoi = 500;
feedFreetext = 510;
engineStart = 550;
engineStop = 560;
engineAvpstart = 570;
tcuWakeup = 600;
tcuSwUpdate = 610;
tcuRcsReset = 620;
tcuInterrogation = 630;
speedalertStart = 710;
speedalertStop = 720;
flshStart = 750; // (DEPRECATED)
flshStop = 760; // (DEPRECATED)
sigposStart = 770;
contractConfigure = 800;
contractRemove = 810;
rootConfigure = 820;
rootRemove = 830;
tripcomp = 850; // reset tripcomp
maintenanceConfigure = 930;
maintenanceComputerOffset = 931;
shorttestExecute = 935;
serviceactivationConfigure = 940;
dc2ServiceactivationConfigure = 945;
dc2RawDownload = 950; //(test command)
applicationConfiguration = 955; // (DC2+)
dc2StartTracking = 960; // (test command)
atpSequence = 990;
theftalarmToggleInterior = 1000;
theftalarmToggleTow = 1010;
theftalarmSelectInteriorTow = 1020;
theftalarmDeselectInteriorTow = 1030;
theftalarmStop = 1040;
windowOpen = 1100;
windowClose = 1110;
windowVentilate = 1120;
windowMove = 1121;
roofOpen = 1130;
roofClose = 1140;
roofLift = 1150;
roofMove = 1151;
batteryMaxsoc = 2000;
batteryChargeprogram = 2010;
chargeprogramconfigure = 2020;
onboardfencesCreate = 2100;
onboardfencesUpdate = 2110;
onboardfencesDelete = 2120;
speedfencesCreate = 2200;
speedfencesUpdate = 2210;
speedfencesDelete = 2220;
chargingtariffsCreate = 2300;
chargingtariffsUpdate = 2310;
chargingtariffsDelete = 2320;
theftalarmstart = 2500;
theftalarmselectinterior = 2510;
theftalarmdeselectinterior = 2520;
theftalarmselecttow = 2530;
theftalarmdeselecttow = 2540;
theftalarmselectdamagedetection = 2550;
theftalarmdeselectdamagedetection = 2560;
theftalarmconfirmdamagedetection = 2570;
mecall2start = 2600;
udxTriggerSynchronization = 1200;
udxActiveUserProfile = 1210;
udxResetUserData = 1220;
userProfSynch = 1230;
userDataReset = 1240;
profActivationSnap = 1250;
profActivationDirect = 1255;
softwareUpdate = 1260;
pushNotification = 1270;
mecallcommand = 1310;
precondStartRcs = 1400;
precondStopRcs = 1410;
precondConfigureRcs = 1420;
tcuConfigure = 1430;
edisonServiceActivation = 1431;
testSequence = 1432;
precondConfigureRacp = 1433;
chargeoptConfigureRacp = 1434;
tariffTableDownload = 1435;
precondStartRacp = 1436;
precondStopRacp = 1437;
rootCertificateRemove = 1438;
onRequestProbeUpload = 1439;
rootCertificateDownload = 1440;
contractCertificateRemove = 1441;
contractCertificateDownload = 1442;
probeConfigurationUpdate = 1443;
rdiagDeleteEcu = 1500;
rdiagStatusReport = 1501;
rdiagExecution = 1502;
immobilizerChallenge = 1600;
immobilizerSearchKeyline = 1610;
immobilizerReleaseKeyline = 1620;
immobilizerLockKeyline = 1630;
immobilizerLockVehicle = 1631;
immobilizerReleaseVehicle = 1621;
setRentalSignal = 1700;
blackchannelDownload = 1800;
blackchannelUpload = 1810;
configurecsm = 1900;
updatevehicleinfo = 1901;
relaymessagetocsm = 1902;
relayrentalrequesttocsb = 1903;
rtmDownloadConfig = 2400;
rtmReadConfig = 2410;
avpActivate = 2700;
chargecontrolconfigure = 2800;
}
}

View File

@ -0,0 +1,46 @@
syntax = "proto3";
package proto;
import "protos.proto";
import "service-activation.proto";
import "user-events.proto";
import "vehicle-commands.proto";
import "vehicle-events.proto";
import "vehicleapi.proto";
option java_package = "com.daimler.mbcarkit.proto";
// message that is sent from the client
// Sending direction: App -> Websocket (-> AppTwin)
message ClientMessage {
reserved 8;
string tracking_id = 5;
oneof msg {
UnsubscribeRequest unsubscribeRequest = 2;
CommandRequest commandRequest = 3;
TrackingEvent tracking_event = 4;
ConfigurePingInterval ping_interval = 6;
AcknowledgeVEPRequest acknowledge_vep_request = 7;
AcknowledgeServiceStatusUpdatesByVIN acknowledge_service_status_updates_by_vin = 9;
AcknowledgeServiceStatusUpdate acknowledge_service_status_update = 13;
AcknowledgeUserDataUpdate acknowledge_user_data_update = 10;
AcknowledgeUserPictureUpdate acknowledge_user_picture_update = 11;
AcknowledgeUserPINUpdate acknowledge_user_pin_update = 12;
UpdateUserJWTRequest update_user_jwt_request = 14;
AcknowledgeUserVehicleAuthChangedUpdate acknowledge_user_vehicle_auth_changed_update = 15;
AcknowledgeAbilityToGetVehicleMasterDataFromRestAPI acknowledge_ability_to_get_vehicle_master_data_from_rest_api = 16;
AcknowledgeVehicleUpdated acknowledge_vehicle_updated = 17;
AcknowledgePreferredDealerChange acknowledge_preferred_dealer_change = 18;
AcknowledgeAppTwinCommandStatusUpdatesByVIN acknowledge_apptwin_command_status_update_by_vin = 19;
Logout logout = 20;
AppTwinPendingCommandsResponse apptwin_pending_commands_response = 21;
AcknowledgeVEPUpdatesByVIN acknowledge_vep_updates_by_vin = 22;
AcknowledgeAssignedVehicles acknowledge_assigned_vehicles = 23;
}
}
// Message to send from the app right before logging out of keycloak
// Stops the corresponding AppTwin actor and shuts it down and
// stops the websocket actor (but does not shut it down. This automatically happens, when the websocket connection is terminated)
message Logout {
}

View File

@ -0,0 +1,15 @@
syntax = "proto3";
package proto;
option java_package = "com.daimler.mbcarkit.proto";
enum MemberStatus {
UNKNOWN_MEMBER_STATUS = 0;
STARTING = 1;
READY = 2;
STOPPING = 3;
}
message AppTwinMemberStatusValue {
MemberStatus status = 1;
uint32 apptwin_count = 2;
}

View File

@ -0,0 +1,22 @@
syntax = "proto3";
package proto;
import "acp.proto";
//import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "gogo.proto";
option (gogoproto.goproto_enum_prefix_all) = true;
option java_package = "com.daimler.mbcarkit.proto";
message EventPushCommand {
string vin = 1 [json_name = "vin"];
VVA.CommandState state = 2 [json_name = "acpState"];
VVA.CommandCondition condition = 3 [json_name = "acpCondition"];
ACP.CommandType type = 4 [json_name = "acpCommandType"];
int64 process_id = 5 [json_name = "pid"];
string tracking_id = 6 [json_name = "trackingId"];
string correlation_id = 7 [json_name = "correlationId"];
repeated int32 error_codes = 8 [json_name = "errorCodes"];
string guid = 9 [json_name = "guid"];
int64 timestamp_in_s = 10 [ json_name = "timestamp" ];
}

View File

@ -0,0 +1,144 @@
// Protocol Buffers for Go with Gadgets
//
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
// http://github.com/gogo/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
package gogoproto;
import "google/protobuf/descriptor.proto";
option java_package = "com.google.protobuf";
option java_outer_classname = "GoGoProtos";
option go_package = "github.com/gogo/protobuf/gogoproto";
extend google.protobuf.EnumOptions {
optional bool goproto_enum_prefix = 62001;
optional bool goproto_enum_stringer = 62021;
optional bool enum_stringer = 62022;
optional string enum_customname = 62023;
optional bool enumdecl = 62024;
}
extend google.protobuf.EnumValueOptions {
optional string enumvalue_customname = 66001;
}
extend google.protobuf.FileOptions {
optional bool goproto_getters_all = 63001;
optional bool goproto_enum_prefix_all = 63002;
optional bool goproto_stringer_all = 63003;
optional bool verbose_equal_all = 63004;
optional bool face_all = 63005;
optional bool gostring_all = 63006;
optional bool populate_all = 63007;
optional bool stringer_all = 63008;
optional bool onlyone_all = 63009;
optional bool equal_all = 63013;
optional bool description_all = 63014;
optional bool testgen_all = 63015;
optional bool benchgen_all = 63016;
optional bool marshaler_all = 63017;
optional bool unmarshaler_all = 63018;
optional bool stable_marshaler_all = 63019;
optional bool sizer_all = 63020;
optional bool goproto_enum_stringer_all = 63021;
optional bool enum_stringer_all = 63022;
optional bool unsafe_marshaler_all = 63023;
optional bool unsafe_unmarshaler_all = 63024;
optional bool goproto_extensions_map_all = 63025;
optional bool goproto_unrecognized_all = 63026;
optional bool gogoproto_import = 63027;
optional bool protosizer_all = 63028;
optional bool compare_all = 63029;
optional bool typedecl_all = 63030;
optional bool enumdecl_all = 63031;
optional bool goproto_registration = 63032;
optional bool messagename_all = 63033;
optional bool goproto_sizecache_all = 63034;
optional bool goproto_unkeyed_all = 63035;
}
extend google.protobuf.MessageOptions {
optional bool goproto_getters = 64001;
optional bool goproto_stringer = 64003;
optional bool verbose_equal = 64004;
optional bool face = 64005;
optional bool gostring = 64006;
optional bool populate = 64007;
optional bool stringer = 67008;
optional bool onlyone = 64009;
optional bool equal = 64013;
optional bool description = 64014;
optional bool testgen = 64015;
optional bool benchgen = 64016;
optional bool marshaler = 64017;
optional bool unmarshaler = 64018;
optional bool stable_marshaler = 64019;
optional bool sizer = 64020;
optional bool unsafe_marshaler = 64023;
optional bool unsafe_unmarshaler = 64024;
optional bool goproto_extensions_map = 64025;
optional bool goproto_unrecognized = 64026;
optional bool protosizer = 64028;
optional bool compare = 64029;
optional bool typedecl = 64030;
optional bool messagename = 64033;
optional bool goproto_sizecache = 64034;
optional bool goproto_unkeyed = 64035;
}
extend google.protobuf.FieldOptions {
optional bool nullable = 65001;
optional bool embed = 65002;
optional string customtype = 65003;
optional string customname = 65004;
optional string jsontag = 65005;
optional string moretags = 65006;
optional string casttype = 65007;
optional string castkey = 65008;
optional string castvalue = 65009;
optional bool stdtime = 65010;
optional bool stdduration = 65011;
optional bool wktpointer = 65012;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/structpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "StructProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// `Struct` represents a structured data value, consisting of fields
// which map to dynamically typed values. In some languages, `Struct`
// might be supported by a native representation. For example, in
// scripting languages like JS a struct is represented as an
// object. The details of that representation are described together
// with the proto support for the language.
//
// The JSON representation for `Struct` is JSON object.
message Struct {
// Unordered map of dynamically typed values.
map<string, Value> fields = 1;
}
// `Value` represents a dynamically typed value which can be either
// null, a number, a string, a boolean, a recursive struct value, or a
// list of values. A producer of value is expected to set one of these
// variants. Absence of any variant indicates an error.
//
// The JSON representation for `Value` is JSON value.
message Value {
// The kind of value.
oneof kind {
// Represents a null value.
NullValue null_value = 1;
// Represents a double value.
double number_value = 2;
// Represents a string value.
string string_value = 3;
// Represents a boolean value.
bool bool_value = 4;
// Represents a structured value.
Struct struct_value = 5;
// Represents a repeated `Value`.
ListValue list_value = 6;
}
}
// `NullValue` is a singleton enumeration to represent the null value for the
// `Value` type union.
//
// The JSON representation for `NullValue` is JSON `null`.
enum NullValue {
// Null value.
NULL_VALUE = 0;
}
// `ListValue` is a wrapper around a repeated field of values.
//
// The JSON representation for `ListValue` is JSON array.
message ListValue {
// Repeated field of dynamically typed values.
repeated Value values = 1;
}

View File

@ -0,0 +1,123 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Wrappers for primitive (non-message) types. These types are useful
// for embedding primitives in the `google.protobuf.Any` type and for places
// where we need to distinguish between the absence of a primitive
// typed field and its default value.
//
// These wrappers have no meaningful use within repeated fields as they lack
// the ability to detect presence on individual elements.
// These wrappers have no meaningful use within a map or a oneof since
// individual entries of a map or fields of a oneof can already detect presence.
syntax = "proto3";
package google.protobuf;
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/wrapperspb";
option java_package = "com.google.protobuf";
option java_outer_classname = "WrappersProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// Wrapper message for `double`.
//
// The JSON representation for `DoubleValue` is JSON number.
message DoubleValue {
// The double value.
double value = 1;
}
// Wrapper message for `float`.
//
// The JSON representation for `FloatValue` is JSON number.
message FloatValue {
// The float value.
float value = 1;
}
// Wrapper message for `int64`.
//
// The JSON representation for `Int64Value` is JSON string.
message Int64Value {
// The int64 value.
int64 value = 1;
}
// Wrapper message for `uint64`.
//
// The JSON representation for `UInt64Value` is JSON string.
message UInt64Value {
// The uint64 value.
uint64 value = 1;
}
// Wrapper message for `int32`.
//
// The JSON representation for `Int32Value` is JSON number.
message Int32Value {
// The int32 value.
int32 value = 1;
}
// Wrapper message for `uint32`.
//
// The JSON representation for `UInt32Value` is JSON number.
message UInt32Value {
// The uint32 value.
uint32 value = 1;
}
// Wrapper message for `bool`.
//
// The JSON representation for `BoolValue` is JSON `true` and `false`.
message BoolValue {
// The bool value.
bool value = 1;
}
// Wrapper message for `string`.
//
// The JSON representation for `StringValue` is JSON string.
message StringValue {
// The string value.
string value = 1;
}
// Wrapper message for `bytes`.
//
// The JSON representation for `BytesValue` is JSON string.
message BytesValue {
// The bytes value.
bytes value = 1;
}

View File

@ -0,0 +1,136 @@
syntax = "proto3";
package proto;
option java_package = "com.daimler.mbcarkit.proto";
// SubscriptionRequest is sent to an actor to indicate that the sender wants to subscribe
// to events of specific topics. By convention the "Sender" property of the actor message is the
// Subscriber and will receive the events.
message SubscribeRequest {
// An array of topics for which the Subscriber wants to be notified from the Receiver of this message
repeated string topics = 1;
// indicates whether the previous set of topics should be replaced or whether the content of
// topics should be merged into the already existing set of topics in the publisher actor. E.g. You're already
// subscribed to topics A and B. If you send a SubscribeRequest with B and C:
// replace = true -> you are subscribed to B and C
// replace = false -> you are subscribed to A, B and C
bool replace = 2;
}
// SubscribeResponse is returned by the actor which received a SubscribeRequest. In case of a successful subscription
// success will be true and error_codes empty/nil. In case of an error the errors map will contain
// information that points to the reason for failure. The error map's keys are topics that have resulted in an error.
// The message also contains all successfully subscribed topics under the `subscribed_topics` key.
// By convention if an SubscribeRequest is sent for topics that have already been subscribed to, the SubscribeResponse
// will be successful and no error will be returned.
message SubscribeResponse {
bool success = 1;
map<string, SubscriptionError> errors = 2;
repeated string subscribed_topics = 3;
}
// UnsubscribeRequest is sent to an actor to indicate that the sender wants to unsubscribe
// from events specified by the topics array.
message UnsubscribeRequest {
// An array of topics for which the Subscriber does not want to receive any more messages
repeated string topics = 1;
// Whether the publisher should respond
bool anticipate_response = 2;
}
// UnsubscribeResponse is returned by the actor which received a UnsubscribeRequest. In case of a successful removal,
// success will be true and error_codes empty/nil. In case of an error the errors map will contain
// information that points to the reason for failure. The error map's keys are topics that have resulted in an error.
// The message also contains all successfully subscribed topics under the `unsubscribed_topics` key.
// By convention if an UnsubscribeRequest is sent for topics that have already been unsubscribed from the UnsubscribeResponse
// will be successful and no error will be returned.
message UnsubscribeResponse {
bool success = 1;
map<string, SubscriptionError> errors = 2;
repeated string unsubscribed_topics = 3;
}
enum SubscriptionErrorType {
UNKNOWN = 0;
INVALID_JWT = 1;
}
message SubscriptionError {
repeated SubscriptionErrorType code = 1;
repeated string message = 2; // Optional
}
// Sent from Websocket-Service -> AppTwin
message SubscribeToAppTwinRequest {
string session_id = 1;
string ciam_id = 2;
// additional data
string device_locale = 3;
string app_id = 4;
string app_version = 5;
OperatingSystemName os_name = 6;
string os_version = 7;
string device_model = 8;
string network_carrier = 9;
string sdk_version = 10;
}
message ResubscribeToAppTwinRequest {
string session_id = 1;
string ciam_id = 2;
}
message ResubscribeToAppTwinResponse {
enum ResubscribeResult {
UNKNOWN_ERROR = 0;
SUCCESS = 1;
INVALID_JWT_ERROR = 2;
TARGET_DOES_NOT_EXIST = 3;
}
ResubscribeResult result = 1;
}
enum OperatingSystemName {
UNKNOWN_OPERATING_SYSTEM = 0;
IOS = 1;
ANDROID = 2;
INT_TEST = 3;
MANUAL_TEST = 4;
WEB = 5;
}
// Sent from AppTwin -> Websocket-Service
message SubscribeToAppTwinResponse {
bool success = 1;
SubscriptionErrorType error_code = 2;
}
message UnsubscribeFromAppTwinRequest {
string session_id = 1;
}
message UnsubscribeFromAppTwinResponse {
bool success = 1;
map<string, SubscriptionError> errors = 2;
}
message Heartbeat {}
// This message is used to tell the App which vehicles are assigned to the current user.
// The message is sent when the AppTwin is fully initialized (i.e. when it received the first vcb-response)
//
// The list of VINs is needed when a user gets unassigned from a vehicle while not connected to an AppTwin
// In this case the vehicle would still show in the app the next time the user starts it (see https://appsfactory.atlassian.net/browse/DAIM-3831)
// To prevent this, we tell the App which VINs are assigned via this message
message AssignedVehicles {
repeated string vins = 1;
}
message AcknowledgeAssignedVehicles {}

View File

@ -0,0 +1,45 @@
syntax = "proto3";
package proto;
option java_package = "com.daimler.mbcarkit.proto";
message AcknowledgeServiceStatusUpdatesByVIN {
int32 sequence_number = 1;
}
message AcknowledgeServiceStatusUpdate {
int32 sequence_number = 1;
}
message ServiceStatusUpdatesByVIN {
int32 sequence_number = 1;
// Updates with VinOrFins
map<string, ServiceStatusUpdate> updates = 2;
}
message ServiceStatusUpdate {
int32 sequence_number = 1;
string ciam_id = 7;
// FinOrVin
string vin = 5;
// when was the event emitted? This is the time of the update,
// not when the attributes where changed. To compare attribute changes, you need to look into each attribute timestamp
int64 emit_timestamp = 2;
// When was the event emitted (milliseconds in Unix time)
int64 emit_timestamp_in_ms = 8;
// serviceID -> Status
map<int32, ServiceStatus> updates = 6;
};
enum ServiceStatus {
SERVICE_STATUS_UNKNOWN = 0;
SERVICE_STATUS_ACTIVE = 1;
SERVICE_STATUS_INACTIVE = 2;
SERVICE_STATUS_ACTIVATION_PENDING = 3;
SERVICE_STATUS_DEACTIVATION_PENDING = 4;
}

View File

@ -0,0 +1,153 @@
syntax = "proto3";
package proto;
option java_package = "com.daimler.mbcarkit.proto";
message AcknowledgeUserDataUpdate {
int32 sequence_number = 1;
}
message UserDataUpdate {
int32 sequence_number = 1;
string ciam_id = 2;
// when was the event emitted? This is the time of the update,
// not when the attributes where changed. To compare attribute changes, you need to look into each attribute timestamp
int64 emit_timestamp = 3;
// When was the event emitted (milliseconds in Unix time)
int64 emit_timestamp_in_ms = 8;
CPDUserData old_data = 6;
CPDUserData new_data = 7;
}
message AcknowledgeUserVehicleAuthChangedUpdate {
int32 sequence_number = 1;
}
message AcknowledgeAbilityToGetVehicleMasterDataFromRestAPI {
int32 sequence_number = 1;
}
message UserVehicleAuthChangedUpdate {
int32 sequence_number = 1;
string ciam_id = 2;
// when was the event emitted? This is the time of the update,
// not when the attributes where changed. To compare attribute changes, you need to look into each attribute timestamp
int64 emit_timestamp = 3;
// When was the event emitted (milliseconds in Unix time)
int64 emit_timestamp_in_ms = 8;
}
message CPDUserData {
string ciam_id = 1;
string user_id = 2;
string first_name = 3;
string last_name1 = 4;
string last_name2 = 5;
string title = 6;
string name_prefix = 7;
string middle_initial = 8;
string salutation_code = 9;
string email = 10;
string landline_phone = 11;
string mobile_phone_number = 12;
string created_at = 13;
string created_by = 14;
string updated_at = 15;
string birthday = 28;
string preferred_language_code = 29;
string account_country_code = 30;
// doc says: TODO
string uc_id = 31;
bool vip = 32;
CPDUserAddress address = 33;
CPDUserCommunicationPreference communication_preference = 34;
}
message CPDUserAddress {
string country_code = 1;
string state = 2;
string province = 3;
string street = 4;
string house_no = 5;
string zip_code = 6;
string city = 7;
string street_type = 8;
string house_name = 9;
string floor_no = 10;
string door_no = 11;
string address_line1 = 12;
string address_line2 = 13;
string address_line3 = 14;
string post_office_box = 15;
}
message CPDUserCommunicationPreference {
bool contacted_by_phone = 1;
bool contacted_by_letter = 2;
bool contacted_by_email = 3;
bool contacted_by_sms = 4;
}
message AcknowledgeUserPictureUpdate {
int32 sequence_number = 1;
}
// Sent after a picture upload/change
message UserPictureUpdate {
int32 sequence_number = 1;
// ciam ID
string ciam_id = 5;
// when was the event emitted? This is the time of the update
int64 emit_timestamp = 2;
// When was the event emitted (milliseconds in Unix time)
int64 emit_timestamp_in_ms = 6;
// this timestamp indicates when a message was read from the eventhub
int64 eventhub_receive_timestamp = 3;
// this timestamp indicates when a message was processed in the app twin
int64 apptwin_receive_timestamp = 4;
}
message AcknowledgeUserPINUpdate {
int32 sequence_number = 1;
}
// Sent after a PIN update
message UserPINUpdate {
int32 sequence_number = 1;
// ciam ID
string ciam_id = 5;
// when was the event emitted? This is the time of the update
int64 emit_timestamp = 2;
// When was the event emitted (milliseconds in Unix time)
int64 emit_timestamp_in_ms = 6;
// this timestamp indicates when a message was read from the eventhub
int64 eventhub_receive_timestamp = 3;
// this timestamp indicates when a message was processed in the app twin
int64 apptwin_receive_timestamp = 4;
}
// Contains the refreshed jwt of the user
message UpdateUserJWTRequest {
string jwt = 1;
}
// Ack for the UpdateUserJWTRequest
message AcknowledgeUpdateUserJWTRequest {
}

View File

@ -0,0 +1,611 @@
syntax = "proto3";
package proto;
//import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "gogo.proto";
import "google/protobuf/wrappers.proto";
option (gogoproto.goproto_enum_prefix_all) = true;
option java_package = "com.daimler.mbcarkit.proto";
// Acknowledge the CommandRequest reached the apptwin actor
// Websocket <- Apptwin
message AcknowledgeCommandRequest {
string request_id = 1;
}
// After the command was issued at VVA based on this
// command request the call will get a command request
// correlation message which matches the request id
// with the process id.
// Sending direction: App - BFF -> AppTwin
message CommandRequest {
string vin = 1;
// Set this id to correlate a CommandStatus
// with this command request.
string request_id = 7;
// Temporary backend switch field. Will be removed as soon as all commands are migrated to the VehicleAPI
// This field only needs to be set if the command is supported by both API from our backend. If this field is removed
// don't forget to set the field 36 to reserved.
enum Backend {
VVA = 0; // default value
VehicleAPI = 1;
}
Backend backend = 36;
oneof command {
AuxheatStart auxheat_start = 2;
AuxheatStop auxheat_stop = 3;
AuxheatConfigure auxheat_configure = 4;
DoorsLock doors_lock = 5;
DoorsUnlock doors_unlock = 6;
SunroofOpen sunroof_open = 9;
SunroofClose sunroof_close = 10;
SunroofLift sunroof_lift = 11;
SunroofMove sunroof_move = 47;
WindowsOpen windows_open = 12;
WindowsClose windows_close = 13;
WindowsVentilate windows_ventilate = 43;
WindowsMove windows_move = 44;
EngineStart engine_start = 19;
EngineStop engine_stop = 20;
ZEVPreconditioningStart zev_preconditioning_start = 21;
ZEVPreconditioningStop zev_preconditioning_stop = 22;
ZEVPreconditioningConfigure zev_precondition_configure = 25;
ZEVPreconditioningConfigureSeats zev_precondition_configure_seats = 26;
SpeedalertStart speedalert_start = 23;
SpeedalertStop speedalert_stop = 24;
BatteryChargeProgramConfigure battery_charge_program = 27;
BatteryMaxSocConfigure battery_max_soc = 28;
ChargeProgramConfigure charge_program_configure = 34;
ChargeControlConfigure charge_control_configure = 40;
ChargeOptConfigure charge_opt_configure = 29;
ChargeOptStart charge_opt_start = 30;
ChargeOptStop charge_opt_stop = 31;
TemperatureConfigure temperature_configure = 32;
WeekProfileConfigure week_profile_configure = 33;
WeekProfileConfigureV2 week_profile_configure_v2 = 41;
SigPosStart sigpos_start = 35;
TheftalarmConfirmDamagedetection theftalarm_confirm_damagedetection = 8;
TheftalarmDeselectDamagedetection theftalarm_deselect_damagedetection= 14;
TheftalarmDeselectInterior theftalarm_deselect_interior = 15;
TheftalarmDeselectTow theftalarm_deselect_tow = 16;
TheftalarmSelectDamagedetection theftalarm_select_damagedetection = 17;
TheftalarmSelectInterior theftalarm_select_interior = 18;
TheftalarmSelectTow theftalarm_select_tow = 37;
TheftalarmStart theftalarm_start = 38;
TheftalarmStop theftalarm_stop = 39;
AutomaticValetParkingActivate automatic_valet_parking_activate = 42;
ChargeFlapUnlock charge_flap_unlock = 45;
ChargeCouplerUnlock charge_coupler_unlock = 46;
DeactivateVehicleKeys deactivate_vehicle_keys = 48;
ActivateVehicleKeys activate_vehicle_keys = 49;
}
}
message DeactivateVehicleKeys {
string pin = 1;
int64 expiration_unix = 2;
string expiration_seconds = 3 [ json_name = "expirationSeconds" ];
string expiration_milliseconds = 4 [ json_name = "expirationMilliseconds" ];
}
message ActivateVehicleKeys {
string pin = 1;
int64 expiration_unix = 2;
string expiration_seconds = 3 [ json_name = "expirationSeconds" ];
string expiration_milliseconds = 4 [ json_name = "expirationMilliseconds" ];
}
message AuxheatStart {}
message AuxheatStop {}
message AuxheatConfigure {
enum Selection {
NO_SELECTION = 0;
TIME_1 = 1;
TIME_2 = 2;
TIME_3 = 3;
}
Selection time_selection = 1 [ json_name = "auxheattimeselection" ];
// Minutes from midnight.
int32 time_1 = 2 [ json_name = "auxheattime1" ];
// Minutes from midnight.
int32 time_2 = 3 [ json_name = "auxheattime2" ];
// Minutes from midnight.
int32 time_3 = 4 [ json_name = "auxheattime3" ];
}
enum Door {
// the JSON enum values are lowercase, but lowercase values are not exported in golang, so
// we allow aliasing all values.
option allow_alias = true;
// the lowercase versions are for json (de)serialization purposes only. The upper case version should be the preferred
// enum values to be used in code.
// These definitions need to come before upper case versions
unknown_door = 0;
frontleft = 1;
frontright = 2;
rearleft = 3;
rearright = 4;
trunk = 5;
fuelflap = 6;
chargeflap = 7;
chargecoupler = 8;
UNKNOWN_DOOR = 0;
FRONT_LEFT = 1;
FRONT_RIGHT = 2;
REAR_LEFT = 3;
REAR_RIGHT = 4;
TRUNK = 5;
FUEL_FLAP = 6;
CHARGE_FLAP = 7;
CHARGE_COUPLER = 8;
}
message DoorsLock {
// doors / flaps to unlock (only supported by TCU type RAMSES)
// leave empty to target all doors
repeated Door doors = 1;
}
message DoorsUnlock {
string pin = 1;
// doors / flaps to unlock (only supported by TCU type RAMSES)
// leave empty to target all doors
repeated Door doors = 2;
}
message EngineStart {
string pin = 1;
}
message EngineStop {}
message SunroofOpen {
string pin = 1;
}
message SunroofClose {}
message SunroofLift {
string pin = 1;
}
message SunroofMove {
string pin = 1;
google.protobuf.Int32Value sunroof = 2 [ json_name = "sunroof" ];
google.protobuf.Int32Value sunroof_blind_front = 3 [ json_name = "sunroofblindfront" ];
google.protobuf.Int32Value sunroof_blind_rear = 4 [ json_name = "sunroofblindrear" ];
}
message WindowsOpen {
string pin = 1;
}
message WindowsClose {}
message WindowsVentilate {
string pin = 1;
}
message WindowsMove {
string pin = 1;
google.protobuf.Int32Value front_left = 2 [ json_name = "windowfrontleft" ];
google.protobuf.Int32Value front_right = 3 [ json_name = "windowfrontright" ];
google.protobuf.Int32Value rear_blind = 4 [ json_name = "windowrearblind" ];
google.protobuf.Int32Value rear_left = 5 [ json_name = "windowrearleft" ];
google.protobuf.Int32Value rear_left_blind = 6 [ json_name = "windowrearleftblind" ];
google.protobuf.Int32Value rear_right = 7 [ json_name = "windowrearright" ];
google.protobuf.Int32Value rear_right_blind = 8 [ json_name = "windowrearrightblind" ];
}
message SpeedalertStart {
int32 threshold = 1 [ json_name = "speedAlertThreshold" ];
int64 alert_end_time = 2 [ json_name = "speedAlertEndTime" ];
}
message SpeedalertStop {}
// --- Vehicle API commands
enum ZEVPreconditioningType {
// the JSON enum values are lowercase, but lowercase values are not exported in golang, so
// we allow aliasing all values.
option allow_alias = true;
// the lowercase versions are for json parsing purposes only. The upper case version should be the preferred
// enum values to be used in code.
// These definitions need to come before upper case versions
unknown_zev_preconditioning_command_type = 0;
immediate = 1;
departure = 2;
now = 3;
departureWeekly = 4;
// the uppercase versions are here to have exported values
// The given preconditioning command type is unknown
UNKNOWN_ZEV_PRECONDITIONING_COMMAND_TYPE = 0;
// starts immediate preconditioning
IMMEDIATE = 1;
// starts preconditioning at departure time (requires a departure time to be provided in ZEVPreconditioningStart)
DEPARTURE = 2;
// start right away (departure time is ignored)
NOW = 3;
// starts preconditioning for a configured weekly profile (does NOT require a departure time to be provided)
DEPARTURE_WEEKLY = 4;
}
message ZEVPreconditioningStart {
int32 departure_time = 1 [ json_name = "departuretime" ];
ZEVPreconditioningType type = 2 [ json_name = "type" ];
}
message ZEVPreconditioningStop { ZEVPreconditioningType type = 2 [ json_name = "type" ]; }
// Configure preconditioning
message ZEVPreconditioningConfigure {
enum DepartureTimeMode {
DISABLED = 0;
SINGLE_DEPARTURE = 1;
WEEKLY_DEPARTURE = 2;
}
DepartureTimeMode departure_time_mode = 1 [ json_name = "departureTimeMode" ];
int32 departure_time = 3 [ json_name = "departuretime" ];
}
// Configure which seats should be preconditioned.
// Currently, the only available options are to precondition all seats or only the front-left seat
message ZEVPreconditioningConfigureSeats {
bool front_left = 1 [ json_name = "precondSeatFrontLeft" ];
bool front_right = 2 [ json_name = "precondSeatFrontRight" ];
bool rear_left = 3 [ json_name = "precondSeatRearLeft" ];
bool rear_right = 4 [ json_name = "precondSeatRearRight" ];
}
// Configure the charge program
message BatteryChargeProgramConfigure {
enum ChargeProgram {
DEFAULT = 0;
INSTANT = 1;
}
ChargeProgram charge_program = 1 [ json_name = "chargeprogram" ];
}
// Configure the maximum value for the state of charge of the HV battery
message BatteryMaxSocConfigure {
// Values need to be between 50 and 100 and divisible by ten
int32 max_soc = 1 [ json_name = "maxsoc" ];
}
// Select the given charge program and enables the consumer to configure it.
message ChargeProgramConfigure {
enum ChargeProgram {
DEFAULT_CHARGE_PROGRAM = 0;
// Instant charge program should not be used
// INSTANT_CHARGE_PROGRAM = 1;
HOME_CHARGE_PROGRAM = 2;
WORK_CHARGE_PROGRAM = 3;
}
ChargeProgram charge_program = 1 [ json_name = "chargeprogram" ];
// Values need to be between 50 and 100 and divisible by ten
// Maximum value for the state of charge of the HV battery [in %].
// Valid value range = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
google.protobuf.Int32Value max_soc = 2 [ json_name = "maxsoc" ];
// unlock the plug after charging is finished
// Denotes whether the charge cable should be unlocked automatically if the HV battery is fully charged resp. charged til Max. SoC value.
// true - unlock automatically, false - do not unlock automatically
// can only be used if chargeprogram is set to home or work. Otherwise it will be ignored.
google.protobuf.BoolValue auto_unlock = 3 [ json_name = "autounlock" ];
// automatically switch between home and work program, based on the location of the car
// Denotes whether location based charging should be used.
// true - use location based charging, false - do not use location based charging
// can only be used if chargeprogram is set to home or work. Otherwise it will be ignored.
google.protobuf.BoolValue location_based_charging = 4 [ json_name = "locationbasedcharging" ];
// enable or disable week-profile
// Denotes whether weekly profile (aka. weekprofile) should be used.
// true - use weekly profile, false - do not use weekly profile
// The parameter weekprofile should not be used.
// google.protobuf.BoolValue weekly_profile = 5 [ json_name = "weeklyprofile" ];
// enable or disable clocktimer
google.protobuf.BoolValue clock_timer = 6 [ json_name = "clocktimer" ];
// enable or disable ecocharging
google.protobuf.BoolValue eco_charging = 7 [ json_name = "ecocharging" ];
}
// This is an experimental command
message ChargeControlConfigure {
// Enables/Disables bidrectional charging
google.protobuf.BoolValue bi_charging_enabled = 1 [ json_name = "bidichargingenabled" ];
// Sets the charging power in kW with a resolution of 0.1 kW. The value has an offset of -100 kW. So
// a value of 0 is equivalent to -100 kW.
google.protobuf.FloatValue charging_power = 2 [ json_name = "chargingpower" ];
// must not be above max_soc
google.protobuf.Int32Value min_soc = 3 [ json_name = "minsoc" ];
}
// Provide functionality to initiate a charge optimization configuration
message ChargeOptConfigure {
message Tariff {
enum Rate {
INVALID_PRICE = 0;
LOW_PRICE = 33;
NORMAL_PRICE = 44;
HIGH_PRICE = 66;
}
Rate rate = 1 [ json_name = "rate" ];
// Time in seconds after 00:00
int32 time = 2 [ json_name = "time" ];
}
repeated Tariff weekday_tariff = 1 [ json_name = "weekdaytariff" ];
repeated Tariff weekend_tariff = 2 [ json_name = "weekendtariff" ];
}
// Provide the functionality to start the charge optimization function in the vehicle
message ChargeOptStart {
// empty
}
// Provide the functionality to stop the charge optimization function in the vehicle
message ChargeOptStop {
// empty
}
// Set the temperature points of the vehicle
message TemperatureConfigure {
message TemperaturePoint {
reserved 2;
enum Zone {
// the JSON enum values are lowercase, but lowercase values are not exported in golang, so
// we allow aliasing all values.
option allow_alias = true;
// the lowercase versions are for json parsing purposes only. The upper case version should be the preferred
// enum values to be used in code.
// These definitions need to come before upper case versions
unknown = 0;
frontLeft = 1;
frontRight = 2;
frontCenter = 3;
rearLeft = 4;
rearRight = 5;
rearCenter = 6;
rear2Left = 7;
rear2Right = 8;
rear2Center = 9;
// PLEASE BE AWARE OF THE FOLLOWING BEFORE ADDING NEW ZONES:
// Currently there is a bug in vehicle API, that we need to send the zones in the correct order. Otherwise the request will be rejected.
// The order needs to be like the following:
// Front before rear before rear2
// Left before right - There is no center zone if there are left and right zones in this row
// As this is already the order like specified here, this needs to be considered on adding new zones.
UNKNOWN_ZONE = 0;
FRONT_LEFT = 1;
FRONT_RIGHT = 2;
FRONT_CENTER = 3;
REAR_LEFT = 4;
REAR_RIGHT = 5;
REAR_CENTER = 6;
REAR_2_LEFT = 7;
REAR_2_RIGHT = 8;
REAR_2_CENTER = 9;
// PLEASE BE AWARE OF THE FOLLOWING BEFORE ADDING NEW ZONES:
// Currently there is a bug in vehicle API, that we need to send the zones in the correct order. Otherwise the request will be rejected.
// The order needs to be like the following:
// Front before rear before rear2
// Left before right - There is no center zone if there are left and right zones in this row
// As this is already the order like specified here, this needs to be considered on adding new zones.
}
Zone zone = 1 [ json_name = "zone" ];
double temperature_in_celsius = 3 [ json_name = "temp" ];
}
repeated TemperaturePoint temperature_points = 1 [ json_name = "temperaturePoints" ];
}
// Set the weekprofile for the weekly departure time settings
message WeekProfileConfigure {
message WeeklySetHU {
enum Day {
MONDAY = 0;
TUESDAY = 1;
WEDNESDAY = 2;
THURSDAY = 3;
FRIDAY = 4;
SATURDAY = 5;
SUNDAY = 6;
}
Day day = 1 [ json_name = "day" ];
// Time in minutes after 00:00
int32 time = 2 [ json_name = "time" ];
}
repeated WeeklySetHU weekly_set_hu = 1 [ json_name = "weeklySetHU" ];
}
// Set the week profile for the weekly departure time settings version 2
message WeekProfileConfigureV2 {
// * The whole list of timeProfiles must always be provided
repeated TimeProfile time_profiles = 1 [ json_name = "timeprofiles" ];
}
message TimeProfile {
//=> only if time profile entry is unchanged, do not provide attribute "id" if new profile entry shall be added
// If a new time profile shall be added: do not provide the ID => ID will be set by MIC / vehicle
google.protobuf.Int32Value identifier = 1 [ json_name = "id" ];
// Hour after midnight range [0, 23]
google.protobuf.Int32Value hour = 2 [json_name = "hour" ];
// Minute after full hour range [0, 59]
google.protobuf.Int32Value minute = 3 [json_name = "min" ];
// Days for which the above time should be applied
repeated TimeProfileDay days = 4 [json_name = "day" ];
// Whether this profile entry is active or not
google.protobuf.BoolValue active = 5 [json_name = "active" ];
// If a timeProfile is changed or added the respective applicationId must be provided by the SDK
// 11 = Internal Apps
// 12 = External Apps
int32 application_identifier = 6 [ json_name = "applicationId" ];
}
enum TimeProfileDay {
option allow_alias = true;
// the short versions are for json (en)coding purposes only. The upper case version should be the preferred
// enum values to be used in code.
// These definitions need to come before upper case versions
Mo = 0;
Tu = 1;
We = 2;
Th = 3;
Fr = 4;
Sa = 5;
Su = 6;
MONDAY = 0;
TUESDAY = 1;
WEDNESDAY = 2;
THURSDAY = 3;
FRIDAY = 4;
SATURDAY = 5;
SUNDAY = 6;
}
// Invoke the Remote Vehicle Finder for signalling the vehicles position with lights, horn or panic alarm.
message SigPosStart {
// Value needs to be between 0 and 30. The default is 0.
// Only allowed for RAMSES
int32 horn_repeat = 1 [ json_name = "hornRepeat" ];
// Only allowed for RAMSES
enum HornType {
HORN_OFF = 0;
HORN_LOW_VOLUME = 1;
HORN_HIGH_VOLUME = 2;
}
HornType horn_type = 2 [ json_name = "hornType" ];
// Only allowed for RAMSES
enum LightType {
LIGHT_OFF = 0;
DIPPED_HEAD_LIGHT = 1;
WARNING_LIGHT = 2;
}
LightType light_type = 3 [ json_name = "lightType" ];
// Value needs to be between 0 and 10. It indicates how long the light should be switched on.
int32 sigpos_duration = 4 [ json_name = "sigposDuration" ];
enum SigposType {
LIGHT_ONLY = 0;
HORN_ONLY = 1; // Only allowed for RAMSES
LIGHT_AND_HORN = 2; // Only allowed for RAMSES
PANIC_ALARM = 3; // Only allowed for HERMES
}
SigposType sigpos_type = 5 [ json_name = "sigposType" ];
}
// Confirm the detected parking bump
message TheftalarmConfirmDamagedetection {
// empty
}
// Provide the functionality to deselect the parking damage detection sensor
message TheftalarmDeselectDamagedetection {
// empty
}
// Provide the functionality to deselect the interior protection sensor
message TheftalarmDeselectInterior {
// empty
}
// Provide the functionality to deselect the tow protection sensor
message TheftalarmDeselectTow {
// empty
}
// Provide the functionality to select the parking damage detection sensor
message TheftalarmSelectDamagedetection {
// empty
}
// Provide the functionality to select the interior protection sensor
message TheftalarmSelectInterior {
// empty
}
// Provide the functionality to select the tow protection sensor
message TheftalarmSelectTow {
// empty
}
// Provide the functionality to trigger an alarm that lasts for "alarm_duration" seconds
message TheftalarmStart {
// Specify how many seconds the alarm should be switched on
int32 alarm_duration_in_seconds = 1 [ json_name = "alarmduration" ];
}
// Provide the functionality to deactivate an active/ongoing alarm
message TheftalarmStop {
// empty
}
enum DriveType {
UNKNOWN_DRIVE_TYPE = 0;
PICK_UP = 1;
DROP_OFF = 2;
}
message AutomaticValetParkingActivate {
string booking_id = 1 [ json_name = "bookingId" ];
DriveType drive_type = 2 [ json_name = "driveType" ];
}
message ChargeFlapUnlock {}
message ChargeCouplerUnlock {}

View File

@ -0,0 +1,457 @@
syntax = "proto3";
package proto;
import "service-activation.proto";
import "user-events.proto";
import "vehicle-commands.proto";
import "protos.proto";
import "vehicleapi.proto";
option java_package = "com.daimler.mbcarkit.proto";
// Sending direction: App <- BFF <- AppTwin
message VEPUpdate {
int32 sequence_number = 1;
string vin = 2;
// indicates whether this is a full update of VEP-attributes.
// All attributes cached in the FE should be erased and completely
// replaced by this push.
bool full_update = 15;
// when was the event emitted? This is the time of the update (unix timestamp in seconds), (deprecated)
// not when the attributes where changed. To compare attribute changes, you need to look into each attribute timestamp
int64 emit_timestamp = 10;
// when was the event emitted? This is the time of the update (unix timestamp in milliseconds),
int64 emit_timestamp_in_ms = 14;
// the attribute changes are a list of changed attributes
map<string, VehicleAttributeStatus> attributes = 11;
}
// Part of a VEPUpdate
// Sending direction: App <- BFF <- AppTwin
message VehicleAttributeStatus {
// time of the attribute change in the car as unix timestamp in seconds with UTC timezone (deprecated)
int64 timestamp = 1 [ deprecated = true ];
// time of the attribute change in the car as unix timestamp in milliseconds with UTC timezone
int64 timestamp_in_ms = 10;
bool changed = 2;
int32 status = 3;
enum CombustionConsumptionUnit {
UNSPECIFIED_COMBUSTION_CONSUMPTION_UNIT = 0;
// Liter per 100 km
LITER_PER_100KM = 1;
// Kilometers per liter
KM_PER_LITER = 2;
// Miles Per imperial gallon
MPG_UK = 3;
// Miles Per US gallon
MPG_US = 4;
}
enum ElectricityConsumptionUnit {
UNSPECIFIED_ELECTRICITY_CONSUMPTION_UNIT = 0;
// kWh per 100 km
KWH_PER_100KM = 1;
// Kilometers per kWh
KM_PER_KWH = 2;
// kWh per 100 miles
KWH_PER_100MI = 3;
// miles per kWh
M_PER_KWH = 4;
// Miles per gallon gasoline equivalent
MPGE = 5;
}
enum GasConsumptionUnit {
UNSPECIFIED_GAS_CONSUMPTION_UNIT = 0;
// kG per 100 km
KG_PER_100KM = 1;
// km per kg
KM_PER_KG = 2;
// miles per kg
M_PER_KG = 3;
}
enum SpeedDistanceUnit {
option deprecated = true; // use speed unit / length unit instead
UNSPECIFIED_SPEED_DISTANCE_UNIT = 0;
// km/h, distance unit: km
KM_PER_H = 1;
// mph, distance unit: miles
M_PER_H = 2;
}
enum SpeedUnit {
UNSPECIFIED_SPEED_UNIT = 0;
// kilometers per hour
KM_PER_HOUR = 1;
// miles per hour
M_PER_HOUR = 2;
}
enum DistanceUnit {
UNSPECIFIED_DISTANCE_UNIT = 0;
KILOMETERS = 1;
MILES = 2;
}
enum TemperatureUnit {
UNSPECIFIED_TEMPERATURE_UNIT = 0;
CELSIUS = 1;
FAHRENHEIT = 2;
}
enum PressureUnit {
UNSPECIFIED_PRESSURE_UNIT = 0;
KPA = 1 ;
BAR = 2;
// Pounds per square inch
PSI = 3;
}
enum RatioUnit {
UNSPECIFIED_RATIO_UNIT = 0;
PERCENT = 1;
}
enum ClockHourUnit {
UNSPECIFIED_CLOCK_HOUR_UNIT = 0;
// 12h (AM/PM)
T12H = 1;
// 24h
T24H = 2;
}
// A list of service ids for which this attribute was sent
// this field ist just used backend internally and will always
// be empty when sent out to the client.
repeated int32 service_ids = 30;
string display_value = 11;
oneof display_unit {
CombustionConsumptionUnit combustion_consumption_unit = 12;
GasConsumptionUnit gas_consumption_unit = 13;
ElectricityConsumptionUnit electricity_consumption_unit = 14;
SpeedDistanceUnit speed_distance_unit = 15 [ deprecated = true ]; // use speed unit / length unit instead
SpeedUnit speed_unit = 25;
DistanceUnit distance_unit = 26;
TemperatureUnit temperature_unit = 16;
PressureUnit pressure_unit = 17;
RatioUnit ratio_unit = 18;
ClockHourUnit clock_hour_unit = 19;
}
oneof attribute_type {
int64 int_value = 4;
bool bool_value = 5;
string string_value = 6;
double double_value = 7;
bool nil_value = 8;
string unsupported_value = 9;
TemperaturePointsValue temperature_points_value = 20;
WeekdayTariffValue weekday_tariff_value = 21;
WeekendTariffValue weekend_tariff_value = 22;
StateOfChargeProfileValue state_of_charge_profile_value = 23;
WeeklySettingsHeadUnitValue weekly_settings_head_unit_value = 24;
SpeedAlertConfigurationValue speed_alert_configuration_value = 27;
EcoHistogramValue eco_histogram_value = 28;
WeeklyProfileValue weekly_profile_value = 29;
ChargeProgramsValue charge_programs_value = 31;
}
}
message ChargeProgramsValue {
repeated ChargeProgramParameters charge_program_parameters = 1;
}
enum ChargeProgram {
DEFAULT_CHARGE_PROGRAM = 0;
INSTANT_CHARGE_PROGRAM = 1;
HOME_CHARGE_PROGRAM = 2;
WORK_CHARGE_PROGRAM = 3;
}
message ChargeProgramParameters {
ChargeProgram charge_program = 1 [ json_name = "chargeprogram" ];
// Values need to be between 50 and 100 and divisible by ten
// Maximum value for the state of charge of the HV battery [in %].
// Valid value range = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
int32 max_soc = 2 [ json_name = "maxSoc" ];
// unlock the plug after charging is finished
// Denotes whether the charge cable should be unlocked automatically if the HV battery is fully charged resp. charged til Max. SoC value.
// true - unlock automatically, false - do not unlock automatically
// can only be used if chargeprogram is set to home or work. Otherwise it will be ignored.
bool auto_unlock = 3 [ json_name = "autounlock" ];
// automatically switch between home and work program, based on the location of the car
// Denotes whether location based charging should be used.
// true - use location based charging, false - do not use location based charging
// can only be used if chargeprogram is set to home or work. Otherwise it will be ignored.
bool location_based_charging = 4 [ json_name = "locationbasedcharging" ];
// enable or disable week-profile
// Denotes whether weekly profile (aka. weekprofile) should be used.
// true - use weekly profile, false - do not use weekly profile
bool weekly_profile = 5 [ json_name = "weeklyprofile" ];
bool clockTimer = 6 [ json_name = "clockTimer" ];
int32 max_charging_current = 7 [ json_name = "MaxChargingCurrent" ];
bool eco_charging = 8 [ json_name = "EcoCharging" ];
}
// Same as VehicleAPI.AttributeStatus but with slightly different names. The VehicleAPI.AttributeStatus enum values
// can't be changed because they are used to automatically parse the vehicleAPI responses. Adding type aliases would
// confuse the contributions developers, so we added another attribute status enum
enum AttributeStatus {
// Value is set and valid
VALUE_VALID = 0;
// Value has not yet been received from the vehicle (but sensor etc. should be available)
VALUE_NOT_RECEIVED = 1;
// Value has been retrieved from vehicle but is invalid (marked as invalid by DaiVB backend)
VALUE_INVALID = 3;
// Vehicle does not support this attribute (e.g. does not have the sensor etc.)
VALUE_NOT_AVAILABLE = 4;
}
message WeeklyProfileValue {
bool single_time_profile_entries_activatable = 1 [ json_name = "singleTimeProfileEntriesActivatable" ];
int32 max_number_of_weekly_time_profile_slots = 2 [ json_name = "maxNumberOfWeeklyTimeProfileSlots" ];
int32 max_number_of_time_profiles = 3 [ json_name = "maxNumberOfTimeProfiles" ];
int32 current_number_of_time_profile_slots = 4 [ json_name = "currentNumberOfTimeProfileSlots" ];
int32 current_number_of_time_profiles = 5 [ json_name = "currentNumberOfTimeProfiles" ];
repeated VVRTimeProfile time_profiles = 6 [ json_name = "timeProfiles" ];
}
// VVRTimeProfile is almost identical to the "TimeProfile" message with the exception that the identifier is not optional.
message VVRTimeProfile {
// unique id of this time profile entry
int32 identifier = 1 [ json_name = "id" ];
// Hour after midnight range [0, 23]
int32 hour = 2 [json_name = "hour" ];
// Minute after full hour range [0, 59]
int32 minute = 3 [json_name = "min" ];
// Days for which the above time should be applied
repeated TimeProfileDay days = 4 [json_name = "day" ];
// Whether this profile entry is active or not
bool active = 5 [json_name = "active" ];
// If a timeProfile is changed or added the respective applicationId must be provided by SDK
// 11 = Internal Apps
// 12 = External Apps
int32 application_identifier = 6 [ json_name = "applicationId" ];
}
message EcoHistogramValue {
repeated EcoHistogramBin eco_histogram_bins = 1;
}
message EcoHistogramBin {
double interval = 1;
double value = 2;
}
message SpeedAlertConfigurationValue {
repeated SpeedAlertConfiguration speed_alert_configurations = 1;
}
message SpeedAlertConfiguration {
// Unix timestamp in seconds
int64 end_timestamp_in_s = 1;
// Speed in kilometers per hour
int32 threshold_in_kph = 2;
// threshold value in the users preferred unit
string threshold_display_value = 3;
}
message WeeklySettingsHeadUnitValue {
// Array with 0 to 21 tupels of day (0..6, 0 = Monday, 1= Tuesday, ..) and departure time in min since midnight (0..1439)
repeated WeeklySetting weekly_settings = 1;
}
message WeeklySetting {
int32 day = 1;
int32 minutes_since_midnight = 2;
}
message TemperaturePointsValue {
// Array with 1 to 5 tupels of zone (frontLeft, frontRight, frontCenter, rearRight, rearLeft, rearCenter, rear2center)
// and temperature in °C where 0 means maximum cooling (LOW) and 30 means maximum heating (HIGH)
repeated TemperaturePoint temperature_points = 1;
}
message TemperaturePoint {
string zone = 1;
double temperature = 2;
string temperature_display_value = 3;
}
message WeekdayTariffValue {
// List of sampling points. Hint: Array will be empty in initial state. I. e.: rate and time will not be existent in initial state.
repeated Tariff tariffs = 1;
}
message WeekendTariffValue {
// List of sampling points. Hint: Array will be empty in initial state. I. e.: rate and time will not be existent in initial state.
repeated Tariff tariffs = 1;
}
message Tariff {
// 33 - off-peak, 44 - mid-peak, 66 - on-peak
int32 rate = 1;
// Seconds from midnight
int32 time = 2;
}
message StateOfChargeProfileValue {
// Array with tupels of state of charge and time offset related to the timestamp of the attribute,
// e.g. [{t, soc}, {t, soc}, .., {t, soc}] (every soc with value range 0..100, every timestamp in seconds, UTC)
repeated StateOfCharge states_of_charge = 1;
}
message StateOfCharge {
// timestamp in seconds, UTC
int64 timestamp_in_s = 1;
// soc with value range 0..100
int32 state_of_charge = 2;
}
// Sending direction: App <- BFF <- AppTwin
message VEPUpdatesByVIN {
int32 sequence_number = 2;
// VIN -> Update
map<string, VEPUpdate> updates = 1;
}
// Sending direction: App <- BFF
message DebugMessage {
string message = 1;
}
// Represents a status response from the
// VVA backend for a given VIN and CIAM ID.
message VehicleStatus {
string vin = 1;
map<string, VehicleAttributeStatus> attributes = 2;
}
// message that is pushed from the vep status service
// Sending direction: App <- BFF
message PushMessage {
reserved 7,8;
string tracking_id = 5;
oneof msg {
VEPUpdate vepUpdate = 1;
VEPUpdatesByVIN vepUpdates = 2;
DebugMessage debugMessage = 3;
ServiceStatusUpdatesByVIN service_status_updates = 9;
ServiceStatusUpdate service_status_update = 13;
UserDataUpdate user_data_update = 10;
UserVehicleAuthChangedUpdate user_vehicle_auth_changed_update = 14;
UserPictureUpdate user_picture_update = 11;
UserPINUpdate user_pin_update = 12;
VehicleUpdated vehicle_updated = 15;
PreferredDealerChange preferred_dealer_change = 16;
AppTwinCommandStatusUpdatesByVIN apptwin_command_status_updates_by_vin = 17;
AppTwinPendingCommandsRequest apptwin_pending_command_request = 18;
AssignedVehicles assigned_vehicles = 19;
}
}
// message type to track an event, e.g. a user interaction with content
// Sending direction: App -> BFF
message TrackingEvent {
// a unique id associated with this event
string tracking_id = 1;
// the unix epoch time in nanoseconds when the event occurred
int64 timestamp = 2;
// a unique identifier describing a single interaction or event
string event_type = 3;
// additional meta data describing the event
map<string, PayloadValue> payload = 4;
}
message PayloadValue {
oneof msg {
string string_value = 1;
int32 int_value = 2;
bool bool_value = 3;
double double_value = 4;
}
}
// acknowledge that the VEP updates of up to `sequenceNumber` have been received
// Sending direction: App -> BFF -> AppTwin
message AcknowledgeVEPRequest {
// This message will be replaced by AcknowledgeVEPUpdatesByVIN
option deprecated = true;
int32 sequence_number = 1;
}
// acknowledge that the VEP updates by vin of up to `sequenceNumber` have been received
// Sending direction: App -> BFF -> AppTwin
// This message should replace the AcknowledgeVEPRequest
message AcknowledgeVEPUpdatesByVIN {
int32 sequence_number = 1;
}
// the client can optionally send this message to reconfigure the ping interval
// Sending direction: App -> BFF
message ConfigurePingInterval {
int32 ping_time_millis = 1;
}
message AcknowledgeVehicleUpdated {
int32 sequence_number = 1;
}
message AcknowledgePreferredDealerChange {
int32 sequence_number = 1;
}
message VehicleUpdated {
int32 sequence_number = 1;
string ciam_id = 2;
string vin = 3;
// When was the event emitted (milliseconds in Unix time)
int64 emit_timestamp_in_ms = 10;
}
message PreferredDealerChange {
int32 sequence_number = 1;
string ciam_id = 2;
string vin = 3;
// When was the event emitted (milliseconds in Unix time)
int64 emit_timestamp_in_ms = 10;
}

View File

@ -0,0 +1,156 @@
syntax = "proto3";
package proto;
import "acp.proto";
import "google/protobuf/struct.proto";
//import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "gogo.proto";
option (gogoproto.goproto_enum_prefix_all) = true;
option java_package = "com.daimler.mbcarkit.proto";
// Sending direction: App -> BFF -> AppTwin
message AcknowledgeAppTwinCommandStatusUpdatesByVIN {
int32 sequence_number = 1;
}
// Sending direction: App <- BFF <- AppTwin
message AppTwinCommandStatusUpdatesByVIN {
int32 sequence_number = 1;
// VIN -> Update
map<string, AppTwinCommandStatusUpdatesByPID> updates_by_vin = 2;
}
// Sending direction: App <- BFF <- AppTwin as part of an AppTwinCommandStatusUpdatesByVIN
message AppTwinCommandStatusUpdatesByPID {
string vin = 1;
// Process ID -> Status
map<int64, AppTwinCommandStatus> updates_by_pid = 2;
}
// Sending direction: App <- BFF <- AppTwin as part of an AppTwinCommandStatusUpdatesByPID
message AppTwinCommandStatus {
// The remote vehicleAPI process id of the command.
int64 process_id = 1;
// The id of the command with which the app created it. Only guaranteed to be
// set on the first transmission to the app.
string request_id = 2;
// The initial CommandStatus from the response of the vehicleAPI has a timestamp of
// -1
int64 timestamp_in_ms = 3;
// Potential ACP error if the command request could not be fulfilled
repeated VehicleAPIError errors = 4;
// Potential timestamp until user cannot send login requests. Data in seconds
// since Unix epoch
int64 blocking_time_seconds = 5 [ deprecated = true ];
// Potential amount of failed pin attempts.
int32 pin_attempts = 6 [ deprecated = true ];
// The type of command the AppTwinCommandStatus belongs to
ACP.CommandType type = 7;
// The command state
VehicleAPI.CommandState state = 8;
}
// VehicleAPICommandPostResult is a message type that can be unmarshaled from a POST request against the vehicle API
// for issuing commands.
message VehicleAPICommandPostResult {
// The remote VVA process id of the command.
int64 process_id = 1 [json_name = "processid"];
// Potential ACP error if the command request could not be fulfilled
repeated VehicleAPIError errors = 2 [json_name = "errors"];
// The command state
VehicleAPI.CommandState state = 3 [json_name = "state"];
}
message VehicleAPICommandGetResult {
// List of processes
repeated VehicleAPICommandProcessStatus process = 1 [json_name = "process"];
// Number of enqueued commands in related command queue
int32 queue_count = 2 [json_name = "queuecount"];
// Name of related command queue type
VehicleAPI.QueueType queue_type = 3 [json_name = "queuetype"];
}
message VehicleAPIDataGetResult {
map<string, VehicleAPIAttributeStatus> data = 1;
}
message VehicleAPIAttributeStatus {
// Value of the attribute (can be anything)
google.protobuf.Value value = 3 [json_name = "value"];
// UTC timestamp in milliseconds
int64 timestamp_in_ms = 2 [json_name = "ts"];
// Status of the attribute
VehicleAPI.AttributeStatus Status = 1 [json_name = "status"];
}
message VehicleAPICommandProcessStatus {
repeated VehicleAPIError errors = 1 [json_name = "errors"];
// GUID (RFC 4122)
string instance_id = 2 [json_name = "instanceid"];
// Name of the command
string name = 3 [json_name = "name"];
// Process ID
int64 process_id = 4 [json_name = "processid"];
// The parameters with which the command was requested
// google.protobuf.Value request_parameters = 5
// [ json_name = "requestparameters" ];
// Response parameters as defined by the command
google.protobuf.Value response_parameters = 6 [json_name = "responseparameters"];
// Current processing state
VehicleAPI.CommandState state = 7 [json_name = "state"];
// UTC timestamp in seconds (ISO 9945)
int64 timestamp_in_s = 8 [json_name = "timestamp"];
// Tracking ID. SHOULD be a GUID (RFC 4122)
string tracking_id = 9 [json_name = "trackingid"];
}
message VehicleAPIError {
string code = 1 [json_name = "error-code"];
string message = 2 [json_name = "error-message"];
map<string, google.protobuf.Value> attributes = 3 [json_name = "attributes"];
repeated VehicleAPIError sub_errors = 4 [json_name = "sub-errors"];
}
// AppTwinPendingCommandsRequest is sent from the AppTwin to the app to ask for commands that the app has not yet
// received a finished state for. This request MUST eventually be answered with AppTwinPendingCommandsResponse.
message AppTwinPendingCommandsRequest {}
// AppTwinPendingCommandsResponse is sent from the app to the AppTwin to tell it the commands that haven't been
// "resolved yet" (are not in a finished state). The delivery of this message to the AppTwin will trigger a command
// actor that polls the state for the specified command type and PID.
message AppTwinPendingCommandsResponse {
repeated PendingCommand pending_commands = 1;
}
message PendingCommand {
string vin = 1;
int64 process_id = 2;
string request_id = 3;
ACP.CommandType type = 4;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
package proto;
option java_package = "com.daimler.mbcarkit.proto";
message VINUpdate {
repeated string addedVINs = 1;
repeated string deletedVINs = 2;
}

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 MBition GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,736 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: cluster.proto
// Protobuf Java Version: 4.26.1
package com.daimler.mbcarkit.proto;
public final class Cluster {
private Cluster() {}
static {
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
/* major= */ 4,
/* minor= */ 26,
/* patch= */ 1,
/* suffix= */ "",
Cluster.class.getName());
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
/**
* Protobuf enum {@code proto.MemberStatus}
*/
public enum MemberStatus
implements com.google.protobuf.ProtocolMessageEnum {
/**
* <code>UNKNOWN_MEMBER_STATUS = 0;</code>
*/
UNKNOWN_MEMBER_STATUS(0),
/**
* <code>STARTING = 1;</code>
*/
STARTING(1),
/**
* <code>READY = 2;</code>
*/
READY(2),
/**
* <code>STOPPING = 3;</code>
*/
STOPPING(3),
UNRECOGNIZED(-1),
;
static {
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
/* major= */ 4,
/* minor= */ 26,
/* patch= */ 1,
/* suffix= */ "",
MemberStatus.class.getName());
}
/**
* <code>UNKNOWN_MEMBER_STATUS = 0;</code>
*/
public static final int UNKNOWN_MEMBER_STATUS_VALUE = 0;
/**
* <code>STARTING = 1;</code>
*/
public static final int STARTING_VALUE = 1;
/**
* <code>READY = 2;</code>
*/
public static final int READY_VALUE = 2;
/**
* <code>STOPPING = 3;</code>
*/
public static final int STOPPING_VALUE = 3;
public final int getNumber() {
if (this == UNRECOGNIZED) {
throw new java.lang.IllegalArgumentException(
"Can't get the number of an unknown enum value.");
}
return value;
}
/**
* @param value The numeric wire value of the corresponding enum entry.
* @return The enum associated with the given numeric wire value.
* @deprecated Use {@link #forNumber(int)} instead.
*/
@java.lang.Deprecated
public static MemberStatus valueOf(int value) {
return forNumber(value);
}
/**
* @param value The numeric wire value of the corresponding enum entry.
* @return The enum associated with the given numeric wire value.
*/
public static MemberStatus forNumber(int value) {
switch (value) {
case 0: return UNKNOWN_MEMBER_STATUS;
case 1: return STARTING;
case 2: return READY;
case 3: return STOPPING;
default: return null;
}
}
public static com.google.protobuf.Internal.EnumLiteMap<MemberStatus>
internalGetValueMap() {
return internalValueMap;
}
private static final com.google.protobuf.Internal.EnumLiteMap<
MemberStatus> internalValueMap =
new com.google.protobuf.Internal.EnumLiteMap<MemberStatus>() {
public MemberStatus findValueByNumber(int number) {
return MemberStatus.forNumber(number);
}
};
public final com.google.protobuf.Descriptors.EnumValueDescriptor
getValueDescriptor() {
if (this == UNRECOGNIZED) {
throw new java.lang.IllegalStateException(
"Can't get the descriptor of an unrecognized enum value.");
}
return getDescriptor().getValues().get(ordinal());
}
public final com.google.protobuf.Descriptors.EnumDescriptor
getDescriptorForType() {
return getDescriptor();
}
public static final com.google.protobuf.Descriptors.EnumDescriptor
getDescriptor() {
return com.daimler.mbcarkit.proto.Cluster.getDescriptor().getEnumTypes().get(0);
}
private static final MemberStatus[] VALUES = values();
public static MemberStatus valueOf(
com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
if (desc.getType() != getDescriptor()) {
throw new java.lang.IllegalArgumentException(
"EnumValueDescriptor is not for this type.");
}
if (desc.getIndex() == -1) {
return UNRECOGNIZED;
}
return VALUES[desc.getIndex()];
}
private final int value;
private MemberStatus(int value) {
this.value = value;
}
// @@protoc_insertion_point(enum_scope:proto.MemberStatus)
}
public interface AppTwinMemberStatusValueOrBuilder extends
// @@protoc_insertion_point(interface_extends:proto.AppTwinMemberStatusValue)
com.google.protobuf.MessageOrBuilder {
/**
* <code>.proto.MemberStatus status = 1;</code>
* @return The enum numeric value on the wire for status.
*/
int getStatusValue();
/**
* <code>.proto.MemberStatus status = 1;</code>
* @return The status.
*/
com.daimler.mbcarkit.proto.Cluster.MemberStatus getStatus();
/**
* <code>uint32 apptwin_count = 2;</code>
* @return The apptwinCount.
*/
int getApptwinCount();
}
/**
* Protobuf type {@code proto.AppTwinMemberStatusValue}
*/
public static final class AppTwinMemberStatusValue extends
com.google.protobuf.GeneratedMessage implements
// @@protoc_insertion_point(message_implements:proto.AppTwinMemberStatusValue)
AppTwinMemberStatusValueOrBuilder {
private static final long serialVersionUID = 0L;
static {
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
/* major= */ 4,
/* minor= */ 26,
/* patch= */ 1,
/* suffix= */ "",
AppTwinMemberStatusValue.class.getName());
}
// Use AppTwinMemberStatusValue.newBuilder() to construct.
private AppTwinMemberStatusValue(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
super(builder);
}
private AppTwinMemberStatusValue() {
status_ = 0;
}
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return com.daimler.mbcarkit.proto.Cluster.internal_static_proto_AppTwinMemberStatusValue_descriptor;
}
@java.lang.Override
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
internalGetFieldAccessorTable() {
return com.daimler.mbcarkit.proto.Cluster.internal_static_proto_AppTwinMemberStatusValue_fieldAccessorTable
.ensureFieldAccessorsInitialized(
com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue.class, com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue.Builder.class);
}
public static final int STATUS_FIELD_NUMBER = 1;
private int status_ = 0;
/**
* <code>.proto.MemberStatus status = 1;</code>
* @return The enum numeric value on the wire for status.
*/
@java.lang.Override public int getStatusValue() {
return status_;
}
/**
* <code>.proto.MemberStatus status = 1;</code>
* @return The status.
*/
@java.lang.Override public com.daimler.mbcarkit.proto.Cluster.MemberStatus getStatus() {
com.daimler.mbcarkit.proto.Cluster.MemberStatus result = com.daimler.mbcarkit.proto.Cluster.MemberStatus.forNumber(status_);
return result == null ? com.daimler.mbcarkit.proto.Cluster.MemberStatus.UNRECOGNIZED : result;
}
public static final int APPTWIN_COUNT_FIELD_NUMBER = 2;
private int apptwinCount_ = 0;
/**
* <code>uint32 apptwin_count = 2;</code>
* @return The apptwinCount.
*/
@java.lang.Override
public int getApptwinCount() {
return apptwinCount_;
}
private byte memoizedIsInitialized = -1;
@java.lang.Override
public final boolean isInitialized() {
byte isInitialized = memoizedIsInitialized;
if (isInitialized == 1) return true;
if (isInitialized == 0) return false;
memoizedIsInitialized = 1;
return true;
}
@java.lang.Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
if (status_ != com.daimler.mbcarkit.proto.Cluster.MemberStatus.UNKNOWN_MEMBER_STATUS.getNumber()) {
output.writeEnum(1, status_);
}
if (apptwinCount_ != 0) {
output.writeUInt32(2, apptwinCount_);
}
getUnknownFields().writeTo(output);
}
@java.lang.Override
public int getSerializedSize() {
int size = memoizedSize;
if (size != -1) return size;
size = 0;
if (status_ != com.daimler.mbcarkit.proto.Cluster.MemberStatus.UNKNOWN_MEMBER_STATUS.getNumber()) {
size += com.google.protobuf.CodedOutputStream
.computeEnumSize(1, status_);
}
if (apptwinCount_ != 0) {
size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(2, apptwinCount_);
}
size += getUnknownFields().getSerializedSize();
memoizedSize = size;
return size;
}
@java.lang.Override
public boolean equals(final java.lang.Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue)) {
return super.equals(obj);
}
com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue other = (com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue) obj;
if (status_ != other.status_) return false;
if (getApptwinCount()
!= other.getApptwinCount()) return false;
if (!getUnknownFields().equals(other.getUnknownFields())) return false;
return true;
}
@java.lang.Override
public int hashCode() {
if (memoizedHashCode != 0) {
return memoizedHashCode;
}
int hash = 41;
hash = (19 * hash) + getDescriptor().hashCode();
hash = (37 * hash) + STATUS_FIELD_NUMBER;
hash = (53 * hash) + status_;
hash = (37 * hash) + APPTWIN_COUNT_FIELD_NUMBER;
hash = (53 * hash) + getApptwinCount();
hash = (29 * hash) + getUnknownFields().hashCode();
memoizedHashCode = hash;
return hash;
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseFrom(
java.nio.ByteBuffer data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseFrom(
java.nio.ByteBuffer data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseFrom(
com.google.protobuf.ByteString data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseFrom(
com.google.protobuf.ByteString data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseFrom(
byte[] data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseWithIOException(PARSER, input);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseWithIOException(PARSER, input, extensionRegistry);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseDelimitedFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseDelimitedWithIOException(PARSER, input);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseDelimitedFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseFrom(
com.google.protobuf.CodedInputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseWithIOException(PARSER, input);
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue parseFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseWithIOException(PARSER, input, extensionRegistry);
}
@java.lang.Override
public Builder newBuilderForType() { return newBuilder(); }
public static Builder newBuilder() {
return DEFAULT_INSTANCE.toBuilder();
}
public static Builder newBuilder(com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue prototype) {
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
}
@java.lang.Override
public Builder toBuilder() {
return this == DEFAULT_INSTANCE
? new Builder() : new Builder().mergeFrom(this);
}
@java.lang.Override
protected Builder newBuilderForType(
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
Builder builder = new Builder(parent);
return builder;
}
/**
* Protobuf type {@code proto.AppTwinMemberStatusValue}
*/
public static final class Builder extends
com.google.protobuf.GeneratedMessage.Builder<Builder> implements
// @@protoc_insertion_point(builder_implements:proto.AppTwinMemberStatusValue)
com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValueOrBuilder {
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return com.daimler.mbcarkit.proto.Cluster.internal_static_proto_AppTwinMemberStatusValue_descriptor;
}
@java.lang.Override
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
internalGetFieldAccessorTable() {
return com.daimler.mbcarkit.proto.Cluster.internal_static_proto_AppTwinMemberStatusValue_fieldAccessorTable
.ensureFieldAccessorsInitialized(
com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue.class, com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue.Builder.class);
}
// Construct using com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue.newBuilder()
private Builder() {
}
private Builder(
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
super(parent);
}
@java.lang.Override
public Builder clear() {
super.clear();
bitField0_ = 0;
status_ = 0;
apptwinCount_ = 0;
return this;
}
@java.lang.Override
public com.google.protobuf.Descriptors.Descriptor
getDescriptorForType() {
return com.daimler.mbcarkit.proto.Cluster.internal_static_proto_AppTwinMemberStatusValue_descriptor;
}
@java.lang.Override
public com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue getDefaultInstanceForType() {
return com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue.getDefaultInstance();
}
@java.lang.Override
public com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue build() {
com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
@java.lang.Override
public com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue buildPartial() {
com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue result = new com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue(this);
if (bitField0_ != 0) { buildPartial0(result); }
onBuilt();
return result;
}
private void buildPartial0(com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue result) {
int from_bitField0_ = bitField0_;
if (((from_bitField0_ & 0x00000001) != 0)) {
result.status_ = status_;
}
if (((from_bitField0_ & 0x00000002) != 0)) {
result.apptwinCount_ = apptwinCount_;
}
}
@java.lang.Override
public Builder mergeFrom(com.google.protobuf.Message other) {
if (other instanceof com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue) {
return mergeFrom((com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue)other);
} else {
super.mergeFrom(other);
return this;
}
}
public Builder mergeFrom(com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue other) {
if (other == com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue.getDefaultInstance()) return this;
if (other.status_ != 0) {
setStatusValue(other.getStatusValue());
}
if (other.getApptwinCount() != 0) {
setApptwinCount(other.getApptwinCount());
}
this.mergeUnknownFields(other.getUnknownFields());
onChanged();
return this;
}
@java.lang.Override
public final boolean isInitialized() {
return true;
}
@java.lang.Override
public Builder mergeFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
if (extensionRegistry == null) {
throw new java.lang.NullPointerException();
}
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
switch (tag) {
case 0:
done = true;
break;
case 8: {
status_ = input.readEnum();
bitField0_ |= 0x00000001;
break;
} // case 8
case 16: {
apptwinCount_ = input.readUInt32();
bitField0_ |= 0x00000002;
break;
} // case 16
default: {
if (!super.parseUnknownField(input, extensionRegistry, tag)) {
done = true; // was an endgroup tag
}
break;
} // default:
} // switch (tag)
} // while (!done)
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.unwrapIOException();
} finally {
onChanged();
} // finally
return this;
}
private int bitField0_;
private int status_ = 0;
/**
* <code>.proto.MemberStatus status = 1;</code>
* @return The enum numeric value on the wire for status.
*/
@java.lang.Override public int getStatusValue() {
return status_;
}
/**
* <code>.proto.MemberStatus status = 1;</code>
* @param value The enum numeric value on the wire for status to set.
* @return This builder for chaining.
*/
public Builder setStatusValue(int value) {
status_ = value;
bitField0_ |= 0x00000001;
onChanged();
return this;
}
/**
* <code>.proto.MemberStatus status = 1;</code>
* @return The status.
*/
@java.lang.Override
public com.daimler.mbcarkit.proto.Cluster.MemberStatus getStatus() {
com.daimler.mbcarkit.proto.Cluster.MemberStatus result = com.daimler.mbcarkit.proto.Cluster.MemberStatus.forNumber(status_);
return result == null ? com.daimler.mbcarkit.proto.Cluster.MemberStatus.UNRECOGNIZED : result;
}
/**
* <code>.proto.MemberStatus status = 1;</code>
* @param value The status to set.
* @return This builder for chaining.
*/
public Builder setStatus(com.daimler.mbcarkit.proto.Cluster.MemberStatus value) {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000001;
status_ = value.getNumber();
onChanged();
return this;
}
/**
* <code>.proto.MemberStatus status = 1;</code>
* @return This builder for chaining.
*/
public Builder clearStatus() {
bitField0_ = (bitField0_ & ~0x00000001);
status_ = 0;
onChanged();
return this;
}
private int apptwinCount_ ;
/**
* <code>uint32 apptwin_count = 2;</code>
* @return The apptwinCount.
*/
@java.lang.Override
public int getApptwinCount() {
return apptwinCount_;
}
/**
* <code>uint32 apptwin_count = 2;</code>
* @param value The apptwinCount to set.
* @return This builder for chaining.
*/
public Builder setApptwinCount(int value) {
apptwinCount_ = value;
bitField0_ |= 0x00000002;
onChanged();
return this;
}
/**
* <code>uint32 apptwin_count = 2;</code>
* @return This builder for chaining.
*/
public Builder clearApptwinCount() {
bitField0_ = (bitField0_ & ~0x00000002);
apptwinCount_ = 0;
onChanged();
return this;
}
// @@protoc_insertion_point(builder_scope:proto.AppTwinMemberStatusValue)
}
// @@protoc_insertion_point(class_scope:proto.AppTwinMemberStatusValue)
private static final com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue DEFAULT_INSTANCE;
static {
DEFAULT_INSTANCE = new com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue();
}
public static com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue getDefaultInstance() {
return DEFAULT_INSTANCE;
}
private static final com.google.protobuf.Parser<AppTwinMemberStatusValue>
PARSER = new com.google.protobuf.AbstractParser<AppTwinMemberStatusValue>() {
@java.lang.Override
public AppTwinMemberStatusValue parsePartialFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
Builder builder = newBuilder();
try {
builder.mergeFrom(input, extensionRegistry);
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(builder.buildPartial());
} catch (com.google.protobuf.UninitializedMessageException e) {
throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
} catch (java.io.IOException e) {
throw new com.google.protobuf.InvalidProtocolBufferException(e)
.setUnfinishedMessage(builder.buildPartial());
}
return builder.buildPartial();
}
};
public static com.google.protobuf.Parser<AppTwinMemberStatusValue> parser() {
return PARSER;
}
@java.lang.Override
public com.google.protobuf.Parser<AppTwinMemberStatusValue> getParserForType() {
return PARSER;
}
@java.lang.Override
public com.daimler.mbcarkit.proto.Cluster.AppTwinMemberStatusValue getDefaultInstanceForType() {
return DEFAULT_INSTANCE;
}
}
private static final com.google.protobuf.Descriptors.Descriptor
internal_static_proto_AppTwinMemberStatusValue_descriptor;
private static final
com.google.protobuf.GeneratedMessage.FieldAccessorTable
internal_static_proto_AppTwinMemberStatusValue_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\rcluster.proto\022\005proto\"V\n\030AppTwinMemberS" +
"tatusValue\022#\n\006status\030\001 \001(\0162\023.proto.Membe" +
"rStatus\022\025\n\rapptwin_count\030\002 \001(\r*P\n\014Member" +
"Status\022\031\n\025UNKNOWN_MEMBER_STATUS\020\000\022\014\n\010STA" +
"RTING\020\001\022\t\n\005READY\020\002\022\014\n\010STOPPING\020\003B\034\n\032com." +
"daimler.mbcarkit.protob\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
});
internal_static_proto_AppTwinMemberStatusValue_descriptor =
getDescriptor().getMessageTypes().get(0);
internal_static_proto_AppTwinMemberStatusValue_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_proto_AppTwinMemberStatusValue_descriptor,
new java.lang.String[] { "Status", "ApptwinCount", });
descriptor.resolveAllFeaturesImmutable();
}
// @@protoc_insertion_point(outer_class_scope)
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,849 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: vin-events.proto
// Protobuf Java Version: 4.26.1
package com.daimler.mbcarkit.proto;
public final class VinEvents {
private VinEvents() {}
static {
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
/* major= */ 4,
/* minor= */ 26,
/* patch= */ 1,
/* suffix= */ "",
VinEvents.class.getName());
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
public interface VINUpdateOrBuilder extends
// @@protoc_insertion_point(interface_extends:proto.VINUpdate)
com.google.protobuf.MessageOrBuilder {
/**
* <code>repeated string addedVINs = 1;</code>
* @return A list containing the addedVINs.
*/
java.util.List<java.lang.String>
getAddedVINsList();
/**
* <code>repeated string addedVINs = 1;</code>
* @return The count of addedVINs.
*/
int getAddedVINsCount();
/**
* <code>repeated string addedVINs = 1;</code>
* @param index The index of the element to return.
* @return The addedVINs at the given index.
*/
java.lang.String getAddedVINs(int index);
/**
* <code>repeated string addedVINs = 1;</code>
* @param index The index of the value to return.
* @return The bytes of the addedVINs at the given index.
*/
com.google.protobuf.ByteString
getAddedVINsBytes(int index);
/**
* <code>repeated string deletedVINs = 2;</code>
* @return A list containing the deletedVINs.
*/
java.util.List<java.lang.String>
getDeletedVINsList();
/**
* <code>repeated string deletedVINs = 2;</code>
* @return The count of deletedVINs.
*/
int getDeletedVINsCount();
/**
* <code>repeated string deletedVINs = 2;</code>
* @param index The index of the element to return.
* @return The deletedVINs at the given index.
*/
java.lang.String getDeletedVINs(int index);
/**
* <code>repeated string deletedVINs = 2;</code>
* @param index The index of the value to return.
* @return The bytes of the deletedVINs at the given index.
*/
com.google.protobuf.ByteString
getDeletedVINsBytes(int index);
}
/**
* Protobuf type {@code proto.VINUpdate}
*/
public static final class VINUpdate extends
com.google.protobuf.GeneratedMessage implements
// @@protoc_insertion_point(message_implements:proto.VINUpdate)
VINUpdateOrBuilder {
private static final long serialVersionUID = 0L;
static {
com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
/* major= */ 4,
/* minor= */ 26,
/* patch= */ 1,
/* suffix= */ "",
VINUpdate.class.getName());
}
// Use VINUpdate.newBuilder() to construct.
private VINUpdate(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
super(builder);
}
private VINUpdate() {
addedVINs_ =
com.google.protobuf.LazyStringArrayList.emptyList();
deletedVINs_ =
com.google.protobuf.LazyStringArrayList.emptyList();
}
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return com.daimler.mbcarkit.proto.VinEvents.internal_static_proto_VINUpdate_descriptor;
}
@java.lang.Override
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
internalGetFieldAccessorTable() {
return com.daimler.mbcarkit.proto.VinEvents.internal_static_proto_VINUpdate_fieldAccessorTable
.ensureFieldAccessorsInitialized(
com.daimler.mbcarkit.proto.VinEvents.VINUpdate.class, com.daimler.mbcarkit.proto.VinEvents.VINUpdate.Builder.class);
}
public static final int ADDEDVINS_FIELD_NUMBER = 1;
@SuppressWarnings("serial")
private com.google.protobuf.LazyStringArrayList addedVINs_ =
com.google.protobuf.LazyStringArrayList.emptyList();
/**
* <code>repeated string addedVINs = 1;</code>
* @return A list containing the addedVINs.
*/
public com.google.protobuf.ProtocolStringList
getAddedVINsList() {
return addedVINs_;
}
/**
* <code>repeated string addedVINs = 1;</code>
* @return The count of addedVINs.
*/
public int getAddedVINsCount() {
return addedVINs_.size();
}
/**
* <code>repeated string addedVINs = 1;</code>
* @param index The index of the element to return.
* @return The addedVINs at the given index.
*/
public java.lang.String getAddedVINs(int index) {
return addedVINs_.get(index);
}
/**
* <code>repeated string addedVINs = 1;</code>
* @param index The index of the value to return.
* @return The bytes of the addedVINs at the given index.
*/
public com.google.protobuf.ByteString
getAddedVINsBytes(int index) {
return addedVINs_.getByteString(index);
}
public static final int DELETEDVINS_FIELD_NUMBER = 2;
@SuppressWarnings("serial")
private com.google.protobuf.LazyStringArrayList deletedVINs_ =
com.google.protobuf.LazyStringArrayList.emptyList();
/**
* <code>repeated string deletedVINs = 2;</code>
* @return A list containing the deletedVINs.
*/
public com.google.protobuf.ProtocolStringList
getDeletedVINsList() {
return deletedVINs_;
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @return The count of deletedVINs.
*/
public int getDeletedVINsCount() {
return deletedVINs_.size();
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @param index The index of the element to return.
* @return The deletedVINs at the given index.
*/
public java.lang.String getDeletedVINs(int index) {
return deletedVINs_.get(index);
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @param index The index of the value to return.
* @return The bytes of the deletedVINs at the given index.
*/
public com.google.protobuf.ByteString
getDeletedVINsBytes(int index) {
return deletedVINs_.getByteString(index);
}
private byte memoizedIsInitialized = -1;
@java.lang.Override
public final boolean isInitialized() {
byte isInitialized = memoizedIsInitialized;
if (isInitialized == 1) return true;
if (isInitialized == 0) return false;
memoizedIsInitialized = 1;
return true;
}
@java.lang.Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
for (int i = 0; i < addedVINs_.size(); i++) {
com.google.protobuf.GeneratedMessage.writeString(output, 1, addedVINs_.getRaw(i));
}
for (int i = 0; i < deletedVINs_.size(); i++) {
com.google.protobuf.GeneratedMessage.writeString(output, 2, deletedVINs_.getRaw(i));
}
getUnknownFields().writeTo(output);
}
@java.lang.Override
public int getSerializedSize() {
int size = memoizedSize;
if (size != -1) return size;
size = 0;
{
int dataSize = 0;
for (int i = 0; i < addedVINs_.size(); i++) {
dataSize += computeStringSizeNoTag(addedVINs_.getRaw(i));
}
size += dataSize;
size += 1 * getAddedVINsList().size();
}
{
int dataSize = 0;
for (int i = 0; i < deletedVINs_.size(); i++) {
dataSize += computeStringSizeNoTag(deletedVINs_.getRaw(i));
}
size += dataSize;
size += 1 * getDeletedVINsList().size();
}
size += getUnknownFields().getSerializedSize();
memoizedSize = size;
return size;
}
@java.lang.Override
public boolean equals(final java.lang.Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof com.daimler.mbcarkit.proto.VinEvents.VINUpdate)) {
return super.equals(obj);
}
com.daimler.mbcarkit.proto.VinEvents.VINUpdate other = (com.daimler.mbcarkit.proto.VinEvents.VINUpdate) obj;
if (!getAddedVINsList()
.equals(other.getAddedVINsList())) return false;
if (!getDeletedVINsList()
.equals(other.getDeletedVINsList())) return false;
if (!getUnknownFields().equals(other.getUnknownFields())) return false;
return true;
}
@java.lang.Override
public int hashCode() {
if (memoizedHashCode != 0) {
return memoizedHashCode;
}
int hash = 41;
hash = (19 * hash) + getDescriptor().hashCode();
if (getAddedVINsCount() > 0) {
hash = (37 * hash) + ADDEDVINS_FIELD_NUMBER;
hash = (53 * hash) + getAddedVINsList().hashCode();
}
if (getDeletedVINsCount() > 0) {
hash = (37 * hash) + DELETEDVINS_FIELD_NUMBER;
hash = (53 * hash) + getDeletedVINsList().hashCode();
}
hash = (29 * hash) + getUnknownFields().hashCode();
memoizedHashCode = hash;
return hash;
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseFrom(
java.nio.ByteBuffer data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseFrom(
java.nio.ByteBuffer data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseFrom(
com.google.protobuf.ByteString data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseFrom(
com.google.protobuf.ByteString data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseFrom(
byte[] data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseWithIOException(PARSER, input);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseWithIOException(PARSER, input, extensionRegistry);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseDelimitedFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseDelimitedWithIOException(PARSER, input);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseDelimitedFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseFrom(
com.google.protobuf.CodedInputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseWithIOException(PARSER, input);
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate parseFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessage
.parseWithIOException(PARSER, input, extensionRegistry);
}
@java.lang.Override
public Builder newBuilderForType() { return newBuilder(); }
public static Builder newBuilder() {
return DEFAULT_INSTANCE.toBuilder();
}
public static Builder newBuilder(com.daimler.mbcarkit.proto.VinEvents.VINUpdate prototype) {
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
}
@java.lang.Override
public Builder toBuilder() {
return this == DEFAULT_INSTANCE
? new Builder() : new Builder().mergeFrom(this);
}
@java.lang.Override
protected Builder newBuilderForType(
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
Builder builder = new Builder(parent);
return builder;
}
/**
* Protobuf type {@code proto.VINUpdate}
*/
public static final class Builder extends
com.google.protobuf.GeneratedMessage.Builder<Builder> implements
// @@protoc_insertion_point(builder_implements:proto.VINUpdate)
com.daimler.mbcarkit.proto.VinEvents.VINUpdateOrBuilder {
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return com.daimler.mbcarkit.proto.VinEvents.internal_static_proto_VINUpdate_descriptor;
}
@java.lang.Override
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
internalGetFieldAccessorTable() {
return com.daimler.mbcarkit.proto.VinEvents.internal_static_proto_VINUpdate_fieldAccessorTable
.ensureFieldAccessorsInitialized(
com.daimler.mbcarkit.proto.VinEvents.VINUpdate.class, com.daimler.mbcarkit.proto.VinEvents.VINUpdate.Builder.class);
}
// Construct using com.daimler.mbcarkit.proto.VinEvents.VINUpdate.newBuilder()
private Builder() {
}
private Builder(
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
super(parent);
}
@java.lang.Override
public Builder clear() {
super.clear();
bitField0_ = 0;
addedVINs_ =
com.google.protobuf.LazyStringArrayList.emptyList();
deletedVINs_ =
com.google.protobuf.LazyStringArrayList.emptyList();
return this;
}
@java.lang.Override
public com.google.protobuf.Descriptors.Descriptor
getDescriptorForType() {
return com.daimler.mbcarkit.proto.VinEvents.internal_static_proto_VINUpdate_descriptor;
}
@java.lang.Override
public com.daimler.mbcarkit.proto.VinEvents.VINUpdate getDefaultInstanceForType() {
return com.daimler.mbcarkit.proto.VinEvents.VINUpdate.getDefaultInstance();
}
@java.lang.Override
public com.daimler.mbcarkit.proto.VinEvents.VINUpdate build() {
com.daimler.mbcarkit.proto.VinEvents.VINUpdate result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
@java.lang.Override
public com.daimler.mbcarkit.proto.VinEvents.VINUpdate buildPartial() {
com.daimler.mbcarkit.proto.VinEvents.VINUpdate result = new com.daimler.mbcarkit.proto.VinEvents.VINUpdate(this);
if (bitField0_ != 0) { buildPartial0(result); }
onBuilt();
return result;
}
private void buildPartial0(com.daimler.mbcarkit.proto.VinEvents.VINUpdate result) {
int from_bitField0_ = bitField0_;
if (((from_bitField0_ & 0x00000001) != 0)) {
addedVINs_.makeImmutable();
result.addedVINs_ = addedVINs_;
}
if (((from_bitField0_ & 0x00000002) != 0)) {
deletedVINs_.makeImmutable();
result.deletedVINs_ = deletedVINs_;
}
}
@java.lang.Override
public Builder mergeFrom(com.google.protobuf.Message other) {
if (other instanceof com.daimler.mbcarkit.proto.VinEvents.VINUpdate) {
return mergeFrom((com.daimler.mbcarkit.proto.VinEvents.VINUpdate)other);
} else {
super.mergeFrom(other);
return this;
}
}
public Builder mergeFrom(com.daimler.mbcarkit.proto.VinEvents.VINUpdate other) {
if (other == com.daimler.mbcarkit.proto.VinEvents.VINUpdate.getDefaultInstance()) return this;
if (!other.addedVINs_.isEmpty()) {
if (addedVINs_.isEmpty()) {
addedVINs_ = other.addedVINs_;
bitField0_ |= 0x00000001;
} else {
ensureAddedVINsIsMutable();
addedVINs_.addAll(other.addedVINs_);
}
onChanged();
}
if (!other.deletedVINs_.isEmpty()) {
if (deletedVINs_.isEmpty()) {
deletedVINs_ = other.deletedVINs_;
bitField0_ |= 0x00000002;
} else {
ensureDeletedVINsIsMutable();
deletedVINs_.addAll(other.deletedVINs_);
}
onChanged();
}
this.mergeUnknownFields(other.getUnknownFields());
onChanged();
return this;
}
@java.lang.Override
public final boolean isInitialized() {
return true;
}
@java.lang.Override
public Builder mergeFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
if (extensionRegistry == null) {
throw new java.lang.NullPointerException();
}
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
switch (tag) {
case 0:
done = true;
break;
case 10: {
java.lang.String s = input.readStringRequireUtf8();
ensureAddedVINsIsMutable();
addedVINs_.add(s);
break;
} // case 10
case 18: {
java.lang.String s = input.readStringRequireUtf8();
ensureDeletedVINsIsMutable();
deletedVINs_.add(s);
break;
} // case 18
default: {
if (!super.parseUnknownField(input, extensionRegistry, tag)) {
done = true; // was an endgroup tag
}
break;
} // default:
} // switch (tag)
} // while (!done)
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.unwrapIOException();
} finally {
onChanged();
} // finally
return this;
}
private int bitField0_;
private com.google.protobuf.LazyStringArrayList addedVINs_ =
com.google.protobuf.LazyStringArrayList.emptyList();
private void ensureAddedVINsIsMutable() {
if (!addedVINs_.isModifiable()) {
addedVINs_ = new com.google.protobuf.LazyStringArrayList(addedVINs_);
}
bitField0_ |= 0x00000001;
}
/**
* <code>repeated string addedVINs = 1;</code>
* @return A list containing the addedVINs.
*/
public com.google.protobuf.ProtocolStringList
getAddedVINsList() {
addedVINs_.makeImmutable();
return addedVINs_;
}
/**
* <code>repeated string addedVINs = 1;</code>
* @return The count of addedVINs.
*/
public int getAddedVINsCount() {
return addedVINs_.size();
}
/**
* <code>repeated string addedVINs = 1;</code>
* @param index The index of the element to return.
* @return The addedVINs at the given index.
*/
public java.lang.String getAddedVINs(int index) {
return addedVINs_.get(index);
}
/**
* <code>repeated string addedVINs = 1;</code>
* @param index The index of the value to return.
* @return The bytes of the addedVINs at the given index.
*/
public com.google.protobuf.ByteString
getAddedVINsBytes(int index) {
return addedVINs_.getByteString(index);
}
/**
* <code>repeated string addedVINs = 1;</code>
* @param index The index to set the value at.
* @param value The addedVINs to set.
* @return This builder for chaining.
*/
public Builder setAddedVINs(
int index, java.lang.String value) {
if (value == null) { throw new NullPointerException(); }
ensureAddedVINsIsMutable();
addedVINs_.set(index, value);
bitField0_ |= 0x00000001;
onChanged();
return this;
}
/**
* <code>repeated string addedVINs = 1;</code>
* @param value The addedVINs to add.
* @return This builder for chaining.
*/
public Builder addAddedVINs(
java.lang.String value) {
if (value == null) { throw new NullPointerException(); }
ensureAddedVINsIsMutable();
addedVINs_.add(value);
bitField0_ |= 0x00000001;
onChanged();
return this;
}
/**
* <code>repeated string addedVINs = 1;</code>
* @param values The addedVINs to add.
* @return This builder for chaining.
*/
public Builder addAllAddedVINs(
java.lang.Iterable<java.lang.String> values) {
ensureAddedVINsIsMutable();
com.google.protobuf.AbstractMessageLite.Builder.addAll(
values, addedVINs_);
bitField0_ |= 0x00000001;
onChanged();
return this;
}
/**
* <code>repeated string addedVINs = 1;</code>
* @return This builder for chaining.
*/
public Builder clearAddedVINs() {
addedVINs_ =
com.google.protobuf.LazyStringArrayList.emptyList();
bitField0_ = (bitField0_ & ~0x00000001);;
onChanged();
return this;
}
/**
* <code>repeated string addedVINs = 1;</code>
* @param value The bytes of the addedVINs to add.
* @return This builder for chaining.
*/
public Builder addAddedVINsBytes(
com.google.protobuf.ByteString value) {
if (value == null) { throw new NullPointerException(); }
checkByteStringIsUtf8(value);
ensureAddedVINsIsMutable();
addedVINs_.add(value);
bitField0_ |= 0x00000001;
onChanged();
return this;
}
private com.google.protobuf.LazyStringArrayList deletedVINs_ =
com.google.protobuf.LazyStringArrayList.emptyList();
private void ensureDeletedVINsIsMutable() {
if (!deletedVINs_.isModifiable()) {
deletedVINs_ = new com.google.protobuf.LazyStringArrayList(deletedVINs_);
}
bitField0_ |= 0x00000002;
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @return A list containing the deletedVINs.
*/
public com.google.protobuf.ProtocolStringList
getDeletedVINsList() {
deletedVINs_.makeImmutable();
return deletedVINs_;
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @return The count of deletedVINs.
*/
public int getDeletedVINsCount() {
return deletedVINs_.size();
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @param index The index of the element to return.
* @return The deletedVINs at the given index.
*/
public java.lang.String getDeletedVINs(int index) {
return deletedVINs_.get(index);
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @param index The index of the value to return.
* @return The bytes of the deletedVINs at the given index.
*/
public com.google.protobuf.ByteString
getDeletedVINsBytes(int index) {
return deletedVINs_.getByteString(index);
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @param index The index to set the value at.
* @param value The deletedVINs to set.
* @return This builder for chaining.
*/
public Builder setDeletedVINs(
int index, java.lang.String value) {
if (value == null) { throw new NullPointerException(); }
ensureDeletedVINsIsMutable();
deletedVINs_.set(index, value);
bitField0_ |= 0x00000002;
onChanged();
return this;
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @param value The deletedVINs to add.
* @return This builder for chaining.
*/
public Builder addDeletedVINs(
java.lang.String value) {
if (value == null) { throw new NullPointerException(); }
ensureDeletedVINsIsMutable();
deletedVINs_.add(value);
bitField0_ |= 0x00000002;
onChanged();
return this;
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @param values The deletedVINs to add.
* @return This builder for chaining.
*/
public Builder addAllDeletedVINs(
java.lang.Iterable<java.lang.String> values) {
ensureDeletedVINsIsMutable();
com.google.protobuf.AbstractMessageLite.Builder.addAll(
values, deletedVINs_);
bitField0_ |= 0x00000002;
onChanged();
return this;
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @return This builder for chaining.
*/
public Builder clearDeletedVINs() {
deletedVINs_ =
com.google.protobuf.LazyStringArrayList.emptyList();
bitField0_ = (bitField0_ & ~0x00000002);;
onChanged();
return this;
}
/**
* <code>repeated string deletedVINs = 2;</code>
* @param value The bytes of the deletedVINs to add.
* @return This builder for chaining.
*/
public Builder addDeletedVINsBytes(
com.google.protobuf.ByteString value) {
if (value == null) { throw new NullPointerException(); }
checkByteStringIsUtf8(value);
ensureDeletedVINsIsMutable();
deletedVINs_.add(value);
bitField0_ |= 0x00000002;
onChanged();
return this;
}
// @@protoc_insertion_point(builder_scope:proto.VINUpdate)
}
// @@protoc_insertion_point(class_scope:proto.VINUpdate)
private static final com.daimler.mbcarkit.proto.VinEvents.VINUpdate DEFAULT_INSTANCE;
static {
DEFAULT_INSTANCE = new com.daimler.mbcarkit.proto.VinEvents.VINUpdate();
}
public static com.daimler.mbcarkit.proto.VinEvents.VINUpdate getDefaultInstance() {
return DEFAULT_INSTANCE;
}
private static final com.google.protobuf.Parser<VINUpdate>
PARSER = new com.google.protobuf.AbstractParser<VINUpdate>() {
@java.lang.Override
public VINUpdate parsePartialFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
Builder builder = newBuilder();
try {
builder.mergeFrom(input, extensionRegistry);
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(builder.buildPartial());
} catch (com.google.protobuf.UninitializedMessageException e) {
throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
} catch (java.io.IOException e) {
throw new com.google.protobuf.InvalidProtocolBufferException(e)
.setUnfinishedMessage(builder.buildPartial());
}
return builder.buildPartial();
}
};
public static com.google.protobuf.Parser<VINUpdate> parser() {
return PARSER;
}
@java.lang.Override
public com.google.protobuf.Parser<VINUpdate> getParserForType() {
return PARSER;
}
@java.lang.Override
public com.daimler.mbcarkit.proto.VinEvents.VINUpdate getDefaultInstanceForType() {
return DEFAULT_INSTANCE;
}
}
private static final com.google.protobuf.Descriptors.Descriptor
internal_static_proto_VINUpdate_descriptor;
private static final
com.google.protobuf.GeneratedMessage.FieldAccessorTable
internal_static_proto_VINUpdate_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\020vin-events.proto\022\005proto\"3\n\tVINUpdate\022\021" +
"\n\taddedVINs\030\001 \003(\t\022\023\n\013deletedVINs\030\002 \003(\tB\034" +
"\n\032com.daimler.mbcarkit.protob\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
});
internal_static_proto_VINUpdate_descriptor =
getDescriptor().getMessageTypes().get(0);
internal_static_proto_VINUpdate_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_proto_VINUpdate_descriptor,
new java.lang.String[] { "AddedVINs", "DeletedVINs", });
descriptor.resolveAllFeaturesImmutable();
}
// @@protoc_insertion_point(outer_class_scope)
}

File diff suppressed because it is too large Load Diff

View File

@ -12,22 +12,32 @@
*/
package org.openhab.binding.mercedesme.internal;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.measure.Unit;
import javax.measure.quantity.Energy;
import javax.measure.quantity.Length;
import javax.measure.quantity.Power;
import javax.measure.quantity.Pressure;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.unit.MetricPrefix;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link Constants} class defines common constants, which are
* {@link Constants} defines common constants, which are
* used across the whole binding.
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class Constants {
public static final String BINDING_VERSION = "oh-release";
public static final String BINDING_ID = "mercedesme";
public static final String COMBUSTION = "combustion";
@ -40,60 +50,282 @@ public class Constants {
public static final ThingTypeUID THING_TYPE_HYBRID = new ThingTypeUID(BINDING_ID, HYBRID);
public static final ThingTypeUID THING_TYPE_BEV = new ThingTypeUID(BINDING_ID, BEV);
public static final String GROUP_RANGE = "range";
public static final int REQUEST_TIMEOUT_MS = 10_000;
public static final Set<ThingTypeUID> DISCOVERABLE_DEVICE_TYPE_UIDS = Collections
.unmodifiableSet(Stream.of(THING_TYPE_COMB, THING_TYPE_HYBRID, THING_TYPE_BEV).collect(Collectors.toSet()));
public static final String MB_KEY_TIRE_SENSOR_AVAILABLE = "tireSensorAvailable";
public static final String MB_KEY_CHARGE_COUPLER_DC_LOCK_STATUS = "chargeCouplerDCLockStatus";
public static final String MB_KEY_CHARGE_COUPLER_DC_STATUS = "chargeCouplerDCStatus";
public static final String MB_KEY_CHARGE_COUPLER_AC_STATUS = "chargeCouplerACStatus";
public static final String MB_KEY_CHARGE_FLAP_DC_STATUS = "chargeFlapDCStatus";
public static final String MB_KEY_SERVICEINTERVALDAYS = "serviceintervaldays";
public static final String MB_KEY_TIREWARNINGSRDK = "tirewarningsrdk";
public static final String MB_KEY_STARTER_BATTERY_STATE = "starterBatteryState";
public static final String MB_KEY_FLIP_WINDOW_STATUS = "flipWindowStatus";
public static final String MB_KEY_WINDOW_STATUS_REAR_BLIND = "windowStatusRearBlind";
public static final String MB_KEY_WINDOW_STATUS_REAR_LEFT_BLIND = "windowStatusRearLeftBlind";
public static final String MB_KEY_WINDOW_STATUS_REAR_RIGHT_BLIND = "windowStatusRearRightBlind";
public static final String MB_KEY_WINDOWSTATUSREARRIGHT = "windowstatusrearright";
public static final String MB_KEY_WINDOWSTATUSREARLEFT = "windowstatusrearleft";
public static final String MB_KEY_WINDOWSTATUSFRONTRIGHT = "windowstatusfrontright";
public static final String MB_KEY_WINDOWSTATUSFRONTLEFT = "windowstatusfrontleft";
public static final String MB_KEY_ROOFTOPSTATUS = "rooftopstatus";
public static final String MB_KEY_SUNROOF_STATUS_REAR_BLIND = "sunroofStatusRearBlind";
public static final String MB_KEY_SUNROOF_STATUS_FRONT_BLIND = "sunroofStatusFrontBlind";
public static final String MB_KEY_SUNROOFSTATUS = "sunroofstatus";
public static final String MB_KEY_IGNITIONSTATE = "ignitionstate";
public static final String MB_KEY_DOOR_STATUS_OVERALL = "doorStatusOverall";
public static final String MB_KEY_WINDOW_STATUS_OVERALL = "windowStatusOverall";
public static final String MB_KEY_DOOR_LOCK_STATUS_OVERALL = "doorLockStatusOverall";
public static final String MB_KEY_TIRE_MARKER_FRONT_RIGHT = "tireMarkerFrontRight";
public static final String MB_KEY_TIRE_MARKER_FRONT_LEFT = "tireMarkerFrontLeft";
public static final String MB_KEY_TIRE_MARKER_REAR_RIGHT = "tireMarkerRearRight";
public static final String MB_KEY_TIRE_MARKER_REAR_LEFT = "tireMarkerRearLeft";
public static final String MB_KEY_PARKBRAKESTATUS = "parkbrakestatus";
public static final String MB_KEY_PRECOND_NOW = "precondNow";
public static final String MB_KEY_PRECOND_SEAT_FRONT_RIGHT = "precondSeatFrontRight";
public static final String MB_KEY_PRECOND_SEAT_FRONT_LEFT = "precondSeatFrontLeft";
public static final String MB_KEY_PRECOND_SEAT_REAR_RIGHT = "precondSeatRearRight";
public static final String MB_KEY_PRECOND_SEAT_REAR_LEFT = "precondSeatRearLeft";
public static final String MB_KEY_WARNINGBRAKEFLUID = "warningbrakefluid";
public static final String MB_KEY_WARNINGBRAKELININGWEAR = "warningbrakeliningwear";
public static final String MB_KEY_WARNINGWASHWATER = "warningwashwater";
public static final String MB_KEY_WARNINGCOOLANTLEVELLOW = "warningcoolantlevellow";
public static final String MB_KEY_WARNINGENGINELIGHT = "warningenginelight";
public static final String MB_KEY_CHARGINGACTIVE = "chargingactive";
public static final String MB_KEY_DOORLOCKSTATUSFRONTRIGHT = "doorlockstatusfrontright";
public static final String MB_KEY_DOORLOCKSTATUSFRONTLEFT = "doorlockstatusfrontleft";
public static final String MB_KEY_DOORLOCKSTATUSREARRIGHT = "doorlockstatusrearright";
public static final String MB_KEY_DOORLOCKSTATUSREARLEFT = "doorlockstatusrearleft";
public static final String MB_KEY_DOORLOCKSTATUSDECKLID = "doorlockstatusdecklid";
public static final String MB_KEY_DOORLOCKSTATUSGAS = "doorlockstatusgas";
public static final String MB_KEY_TIREPRESSURE_FRONT_LEFT = "tirepressureFrontLeft";
public static final String MB_KEY_TIREPRESSURE_FRONT_RIGHT = "tirepressureFrontRight";
public static final String MB_KEY_TIREPRESSURE_REAR_LEFT = "tirepressureRearLeft";
public static final String MB_KEY_POSITION_HEADING = "positionHeading";
public static final String MB_KEY_TIREPRESSURE_REAR_RIGHT = "tirepressureRearRight";
public static final String MB_KEY_ENGINE_HOOD_STATUS = "engineHoodStatus";
public static final String MB_KEY_DECKLIDSTATUS = "decklidstatus";
public static final String MB_KEY_DOORSTATUSREARLEFT = "doorstatusrearleft";
public static final String MB_KEY_DOORSTATUSREARRIGHT = "doorstatusrearright";
public static final String MB_KEY_DOORSTATUSFRONTLEFT = "doorstatusfrontleft";
public static final String MB_KEY_DOORSTATUSFRONTRIGHT = "doorstatusfrontright";
public static final String MB_KEY_TANKLEVELPERCENT = "tanklevelpercent";
public static final String MB_KEY_SOC = "soc";
public static final String MB_KEY_TIRE_PRESS_MEAS_TIMESTAMP = "tirePressMeasTimestamp";
public static final String MB_KEY_ENDOFCHARGETIME = "endofchargetime";
public static final String MB_KEY_LIQUIDCONSUMPTIONRESET = "liquidconsumptionreset";
public static final String MB_KEY_LIQUIDCONSUMPTIONSTART = "liquidconsumptionstart";
public static final String MB_KEY_ELECTRICCONSUMPTIONRESET = "electricconsumptionreset";
public static final String MB_KEY_ELECTRICCONSUMPTIONSTART = "electricconsumptionstart";
public static final String MB_KEY_AVERAGE_SPEED_RESET = "averageSpeedReset";
public static final String MB_KEY_AVERAGE_SPEED_START = "averageSpeedStart";
public static final String MB_KEY_CHARGING_POWER = "chargingPower";
public static final String MB_KEY_DRIVEN_TIME_RESET = "drivenTimeReset";
public static final String MB_KEY_DRIVEN_TIME_START = "drivenTimeStart";
public static final String MB_KEY_DISTANCE_RESET = "distanceReset";
public static final String MB_KEY_DISTANCE_START = "distanceStart";
public static final String MB_KEY_RANGELIQUID = "rangeliquid";
public static final String MB_KEY_OVERALL_RANGE = "overallRange";
public static final String MB_KEY_RANGEELECTRIC = "rangeelectric";
public static final String MB_KEY_ODO = "odo";
public static final String MB_KEY_POSITION_LONG = "positionLong";
public static final String MB_KEY_POSITION_LAT = "positionLat";
public static final String MB_KEY_TEMPERATURE_POINTS = "temperaturePoints";
public static final String MB_KEY_SELECTED_CHARGE_PROGRAM = "selectedChargeProgram";
public static final String MB_KEY_CHARGE_PROGRAMS = "chargePrograms";
public static final String MB_KEY_COMMAND_CAPABILITIES = "command-capabilities";
public static final String MB_KEY_FEATURE_CAPABILITIES = "feature-capabilities";
public static final String MB_KEY_COMMAND_ZEV_PRECONDITION_CONFIGURE_SEATS = "commandZevPreconditionConfigureSeats";
public static final String MB_KEY_COMMAND_SUNROOF_OPEN = "commandSunroofOpen";
public static final String MB_KEY_COMMAND_CHARGE_PROGRAM_CONFIGURE = "commandChargeProgramConfigure";
public static final String MB_KEY_COMMAND_SIGPOS_START = "commandSigposStart";
public static final String MB_KEY_FEATURE_AUX_HEAT = "featureAuxHeat";
public static final String MB_KEY_COMMAND_ZEV_PRECONDITIONING_START = "commandZevPreconditioningStart";
public static final String MB_KEY_COMMAND_ZEV_PRECONDITION_CONFIGURE = "commandZevPreconditionConfigure";
public static final String MB_KEY_COMMAND_DOORS_LOCK = "commandDoorsLock";
public static final String MB_KEY_COMMAND_WINDOWS_OPEN = "commandWindowsOpen";
public static final String MB_KEY_COMMAND_ENGINE_START = "commandEngineStart";
public static final String GROUP_VEHICLE = "vehicle";
public static final String GROUP_DOORS = "doors";
public static final String GROUP_WINDOWS = "windows";
public static final String GROUP_LOCK = "lock";
public static final String GROUP_LIGHTS = "lights";
public static final String GROUP_LOCATION = "location";
public static final String GROUP_IMAGE = "image";
public static final String GROUP_WINDOWS = "windows";
public static final String GROUP_HVAC = "hvac";
public static final String GROUP_SERVICE = "service";
public static final String GROUP_RANGE = "range";
public static final String GROUP_CHARGE = "charge";
public static final String GROUP_TRIP = "trip";
public static final String GROUP_POSITION = "position";
public static final String GROUP_TIRES = "tires";
public static final String GROUP_COMMAND = "command";
public static final String MB_AUTH_URL = "https://ssoalpha.dvb.corpinter.net/v1/auth";
public static final String MB_TOKEN_URL = "https://ssoalpha.dvb.corpinter.net/v1/token";
public static final String CALLBACK_ENDPOINT = "/mb-callback";
public static final String OAUTH_CLIENT_NAME = "#byocar";
// https://developer.mercedes-benz.com/products/electric_vehicle_status/docs
public static final String SCOPE_EV = "mb:vehicle:mbdata:evstatus";
// https://developer.mercedes-benz.com/products/fuel_status/docs
public static final String SCOPE_FUEL = "mb:vehicle:mbdata:fuelstatus";
// https://developer.mercedes-benz.com/products/pay_as_you_drive_insurance/docs
public static final String SCOPE_ODO = "mb:vehicle:mbdata:payasyoudrive";
// https://developer.mercedes-benz.com/products/vehicle_lock_status/docs
public static final String SCOPE_LOCK = "mb:vehicle:mbdata:vehiclelock";
// https://developer.mercedes-benz.com/products/vehicle_status/docs
public static final String SCOPE_STATUS = "mb:vehicle:mbdata:vehiclestatus";
public static final String SCOPE_OFFLINE = "offline_access";
public static final String SCOPE_OPENID = "openid";
public static final String BASE_URL = "https://api.mercedes-benz.com/vehicledata/v2";
public static final String ODO_URL = BASE_URL + "/vehicles/%s/containers/payasyoudrive";
public static final String STATUS_URL = BASE_URL + "/vehicles/%s/containers/vehiclestatus";
public static final String LOCK_URL = BASE_URL + "/vehicles/%s/containers/vehiclelockstatus";
public static final String FUEL_URL = BASE_URL + "/vehicles/%s/containers/fuelstatus";
public static final String EV_URL = BASE_URL + "/vehicles/%s/containers/electricvehicle";
public static final String OH_CHANNEL_LAST_UPDATE = "last-update";
public static final String OH_CHANNEL_SENSOR_AVAILABLE = "sensor-available";
public static final String OH_CHANNEL_MARKER_FRONT_LEFT = "marker-front-left";
public static final String OH_CHANNEL_MARKER_REAR_LEFT = "marker-rear-left";
public static final String OH_CHANNEL_MARKER_FRONT_RIGHT = "marker-front-right";
public static final String OH_CHANNEL_MARKER_REAR_RIGHT = "marker-rear-right";
public static final String OH_CHANNEL_PRESSURE_FRONT_LEFT = "pressure-front-left";
public static final String OH_CHANNEL_PRESSURE_REAR_LEFT = "pressure-rear-left";
public static final String OH_CHANNEL_PRESSURE_FRONT_RIGHT = "pressure-front-right";
public static final String OH_CHANNEL_PRESSURE_REAR_RIGHT = "pressure-rear-right";
public static final String OH_CHANNEL_CONS_CONV_RESET = "cons-conv-reset";
public static final String OH_CHANNEL_CONS_EV_RESET = "cons-ev-reset";
public static final String OH_CHANNEL_AVG_SPEED_RESET = "avg-speed-reset";
public static final String OH_CHANNEL_TIME_RESET = "time-reset";
public static final String OH_CHANNEL_DISTANCE_RESET = "distance-reset";
public static final String OH_CHANNEL_CONS_CONV = "cons-conv";
public static final String OH_CHANNEL_CONS_EV = "cons-ev";
public static final String OH_CHANNEL_AVG_SPEED = "avg-speed";
public static final String OH_CHANNEL_TIME = "time";
public static final String OH_CHANNEL_DISTANCE = "distance";
public static final String OH_CHANNEL_HEADING = "heading";
public static final String OH_CHANNEL_END_TIME = "end-time";
public static final String OH_CHANNEL_POWER = "power";
public static final String OH_CHANNEL_COUPLER_LOCK = "coupler-lock";
public static final String OH_CHANNEL_COUPLER_DC = "coupler-dc";
public static final String OH_CHANNEL_COUPLER_AC = "coupler-ac";
public static final String OH_CHANNEL_CHARGE_FLAP = "charge-flap";
public static final String OH_CHANNEL_FUEL_LEVEL = "fuel-level";
public static final String OH_CHANNEL_RANGE_HYBRID = "range-hybrid";
public static final String OH_CHANNEL_RANGE_FUEL = "range-fuel";
public static final String OH_CHANNEL_RANGE_ELECTRIC = "range-electric";
public static final String OH_CHANNEL_RADIUS_HYBRID = "radius-hybrid";
public static final String OH_CHANNEL_RADIUS_FUEL = "radius-fuel";
public static final String OH_CHANNEL_RADIUS_ELECTRIC = "radius-electric";
public static final String OH_CHANNEL_SERVICE_DAYS = "service-days";
public static final String OH_CHANNEL_TIRES_RDK = "tires-rdk";
public static final String OH_CHANNEL_ENGINE = "engine";
public static final String OH_CHANNEL_COOLANT_FLUID = "coolant-fluid";
public static final String OH_CHANNEL_BRAKE_LINING_WEAR = "brake-lining-wear";
public static final String OH_CHANNEL_WASH_WATER = "wash-water";
public static final String OH_CHANNEL_BRAKE_FLUID = "brake-fluid";
public static final String OH_CHANNEL_STARTER_BATTERY = "starter-battery";
public static final String OH_CHANNEL_ACTIVE = "active";
public static final String OH_CHANNEL_FLIP_WINDOW = "flip-window";
public static final String OH_CHANNEL_REAR_BLIND = "rear-blind";
public static final String OH_CHANNEL_REAR_LEFT_BLIND = "rear-left-blind";
public static final String OH_CHANNEL_REAR_RIGHT_BLIND = "rear-right-blind";
public static final String OH_CHANNEL_GAS_FLAP = "gas-flap";
public static final String OH_CHANNEL_ROOFTOP = "rooftop";
public static final String OH_CHANNEL_SUNROOF_REAR_BLIND = "sunroof-rear-blind";
public static final String OH_CHANNEL_SUNROOF_FRONT_BLIND = "sunroof-front-blind";
public static final String OH_CHANNEL_SUNROOF = "sunroof";
public static final String OH_CHANNEL_ENGINE_HOOD = "engine-hood";
public static final String OH_CHANNEL_DECK_LID = "deck-lid";
public static final String OH_CHANNEL_REAR_LEFT = "rear-left";
public static final String OH_CHANNEL_REAR_RIGHT = "rear-right";
public static final String OH_CHANNEL_FRONT_LEFT = "front-left";
public static final String OH_CHANNEL_FRONT_RIGHT = "front-right";
public static final String OH_CHANNEL_PARK_BRAKE = "park-brake";
public static final String OH_CHANNEL_IGNITION = "ignition";
public static final String OH_CHANNEL_DOOR_STATUS = "door-status";
public static final String OH_CHANNEL_WINDOWS = "windows";
public static final String OH_CHANNEL_LOCK = "lock";
public static final String OH_CHANNEL_MILEAGE = "mileage";
public static final String OH_CHANNEL_TEMPERATURE = "temperature";
public static final String OH_CHANNEL_AUX_HEAT = "aux-heat";
public static final String OH_CHANNEL_ZONE = "zone";
public static final String OH_CHANNEL_SIGNAL = "signal";
public static final String OH_CHANNEL_AUTO_UNLOCK = "auto-unlock";
public static final String OH_CHANNEL_MAX_SOC = "max-soc";
public static final String OH_CHANNEL_PROGRAM = "program";
public static final String OH_CHANNEL_CMD_LAST_UPDATE = "cmd-last-update";
public static final String OH_CHANNEL_CMD_STATE = "cmd-state";
public static final String OH_CHANNEL_CMD_NAME = "cmd-name";
public static final String OH_CHANNEL_PROTO_UPDATE = "proto-update";
public static final String OH_CHANNEL_SOC = "soc";
public static final String OH_CHANNEL_UNCHARGED = "uncharged";
public static final String OH_CHANNEL_CHARGED = "charged";
public static final String OH_CHANNEL_TANK_OPEN = "tank-open";
public static final String OH_CHANNEL_TANK_REMAIN = "tank-remain";
public static final String OH_CHANNEL_HOME_DISTANCE = "home-distance";
public static final String OH_CHANNEL_GPS = "gps";
public static final String OH_CHANNEL_CONS_CONV_UNIT = "cons-conv-unit";
public static final String OH_CHANNEL_CONS_EV_UNIT = "cons-ev-unit";
public static final String CALLBACK_ENDPOINT = "/mb-auth";
// https://developer.mercedes-benz.com/content-page/api_migration_guide
public static final String IMAGE_BASE_URL = "https://api.mercedes-benz.com/vehicle_images/v2";
public static final String IMAGE_EXTERIOR_RESOURCE_URL = IMAGE_BASE_URL + "/vehicles/%s";
public static final String STATUS_TEXT_PREFIX = "@text/mercedesme.";
public static final String STATUS_AUTH_NEEDED = ".status.authorization-needed";
public static final String STATUS_EMAIL_MISSING = ".status.email-missing";
public static final String STATUS_REGION_MISSING = ".status.region-missing";
public static final String STATUS_REFRESH_INVALID = ".status.refresh-invalid";
public static final String STATUS_IP_MISSING = ".status.ip-missing";
public static final String STATUS_PORT_MISSING = ".status.port-missing";
public static final String STATUS_CLIENT_ID_MISSING = ".status.client-id-missing";
public static final String STATUS_CLIENT_SECRET_MISSING = ".status.client-secret-missing";
public static final String STATUS_SERVER_RESTART = ".status.server-restart";
public static final String STATUS_BRIDGE_MISSING = ".status.bridge-missing";
public static final String STATUS_BRIDGE_ATHORIZATION = ".status.bridge-authoriziation";
public static final String SPACE = " ";
public static final String EMPTY = "";
public static final String COLON = ":";
public static final String NOT_SET = "not set";
public static final String UNRECOGNIZED = "UNRECOGNIZED";
public static final String CODE = "code";
public static final String GUID = "guid";
public static final String PIN = "pin";
public static final String MIME_PREFIX = "image/";
public static final Unit<Length> KILOMETRE_UNIT = MetricPrefix.KILO(SIUnits.METRE);
public static final Unit<Power> KILOWATT_UNIT = MetricPrefix.KILO(Units.WATT);
public static final Unit<Energy> KILOWATT_HOUR_UNIT = MetricPrefix.KILO(Units.WATT_HOUR);
public static final Unit<Pressure> KPA_UNIT = MetricPrefix.KILO(SIUnits.PASCAL);
public static final String LOGIN_APP_ID = "01398c1c-dc45-4b42-882b-9f5ba9f175f1";
public static final String LOGIN_APP_ID_EU = "01398c1c-dc45-4b42-882b-9f5ba9f175f1";
public static final String LOGIN_APP_ID_CN = "3f36efb1-f84b-4402-b5a2-68a118fec33e";
public static final String LOGIN_BASE_URI = "https://id.mercedes-benz.com";
public static final String LOGIN_BASE_URI_CN = "https://ciam-1.mercedes-benz.com.cn";
public static final String LOGIN_BASE_URI_NA = "https://id.mercedes-benz.com";
public static final String LOGIN_BASE_URI_PA = "https://id.mercedes-benz.com";
public static final String PSAG_BASE_URI = "https://psag.query.api.dvb.corpinter.net";
public static final String PSAG_BASE_URI_CN = "https://psag.query.api.dvb.corpinter.net.cn";
public static final String RCP_BASE_URI = "https://rcp-rs.query.api.dvb.corpinter.net";
public static final String RCP_BASE_URI_CN = "https://rcp-rs.query.api.dvb.corpinter.net.cn";
public static final String REST_API_BASE = "https://bff.emea-prod.mobilesdk.mercedes-benz.com";
public static final String REST_API_BASE_CN = "https://bff.cn-prod.mobilesdk.mercedes-benz.com";
public static final String REST_API_BASE_NA = "https://bff.amap-prod.mobilesdk.mercedes-benz.com";
public static final String REST_API_BASE_PA = "https://bff.amap-prod.mobilesdk.mercedes-benz.com";
public static final String WEBSOCKET_API_BASE = "wss://websocket.emea-prod.mobilesdk.mercedes-benz.com/ws";
public static final String WEBSOCKET_API_BASE_NA = "wss://websocket.amap-prod.mobilesdk.mercedes-benz.com/ws";
public static final String WEBSOCKET_API_BASE_PA = "wss://websocket.amap-prod.mobilesdk.mercedes-benz.com/ws";
public static final String WEBSOCKET_API_BASE_CN = "wss://websocket.cn-prod.mobilesdk.mercedes-benz.com/ws";
public static final String WEBSOCKET_USER_AGENT = "MyCar/1.30.1 (com.daimler.ris.mercedesme.ece.ios; build:1819; iOS 16.5.0) Alamofire/5.4.0";
public static final String WEBSOCKET_USER_AGENT_CN = "MyStarCN/1.27.0 (com.daimler.ris.mercedesme.cn.ios; build:1758; iOS 16.3.1) Alamofire/5.4.0";
public static final String WEBSOCKET_USER_AGENT_PA = "mycar-store-ap v1.27.0, android 8.0.0, SDK 2.84.3";
public static final String RIS_APPLICATION_VERSION_NA = "3.40.0";
public static final String RIS_APPLICATION_VERSION_CN = "1.39.0";
public static final String RIS_APPLICATION_VERSION_PA = "1.40.0";
public static final String RIS_APPLICATION_VERSION = "1.42.0 (2168)";
public static final String RIS_SDK_VERSION = "2.114.0";
public static final String RIS_SDK_VERSION_CN = "2.109.2";
public static final String RIS_OS_VERSION = "17.4.1";
public static final String RIS_OS_NAME = "ios";
public static final String X_APPLICATIONNAME = "mycar-store-ece";
public static final String X_APPLICATIONNAME_ECE = "mycar-store-ece";
public static final String X_APPLICATIONNAME_CN = "mycar-store-cn";
public static final String X_APPLICATIONNAME_US = "mycar-store-us";
public static final String X_APPLICATIONNAME_AP = "mycar-store-ap";
public static final String REGION_EUROPE = "EU";
public static final String REGION_NORAM = "NA";
public static final String REGION_APAC = "AP";
public static final String REGION_CHINA = "CN";
public static final String SCOPE = "openid email phone profile offline_access ciam-uid";
public static final String MAX_SOC_KEY = "maxsoc";
public static final String AUTO_UNLOCK_KEY = "autolock";
public static final String JUNIT_SERVER_ADDR = "http://999.999.999.999:99999/mb-auth";
public static final String JUNIT_TOKEN = "junitTestToken";
public static final String JUNIT_REFRESH_TOKEN = "junitRefreshToken";
}

View File

@ -19,12 +19,19 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.WWWAuthenticationProtocolHandler;
import org.openhab.binding.mercedesme.internal.discovery.MercedesMeDiscoveryService;
import org.openhab.binding.mercedesme.internal.handler.AccountHandler;
import org.openhab.binding.mercedesme.internal.handler.VehicleHandler;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.binding.mercedesme.internal.utils.Mapper;
import org.openhab.binding.mercedesme.internal.utils.Utils;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.items.MetadataRegistry;
import org.openhab.core.net.NetworkAddressService;
import org.openhab.core.storage.StorageService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
@ -32,16 +39,16 @@ import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link MercedesMeHandlerFactory} is responsible for creating things and thing
* handlers.
* The {@link MercedesMeHandlerFactory} is responsible for creating thing handlers.
*
* @author Bernd Weymann - Initial contribution
*/
@ -51,31 +58,38 @@ public class MercedesMeHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BEV, THING_TYPE_COMB,
THING_TYPE_HYBRID, THING_TYPE_ACCOUNT);
private final Logger logger = LoggerFactory.getLogger(MercedesMeHandlerFactory.class);
private final OAuthFactory oAuthFactory;
private final HttpClient httpClient;
private final LocaleProvider localeProvider;
private final LocationProvider locationProvider;
private final StorageService storageService;
private final MercedesMeDiscoveryService discoveryService;
private final MercedesMeCommandOptionProvider mmcop;
private final MercedesMeStateOptionProvider mmsop;
private final StorageService storageService;
private final TimeZoneProvider timeZoneProvider;
private final NetworkAddressService networkService;
private @Nullable ServiceRegistration<?> discoveryServiceReg;
private @Nullable MercedesMeMetadataAdjuster mdAdjuster;
public static String ohVersion = "unknown";
@Activate
public MercedesMeHandlerFactory(@Reference OAuthFactory oAuthFactory, @Reference HttpClientFactory hcf,
@Reference StorageService storageService, final @Reference MercedesMeCommandOptionProvider cop,
final @Reference MercedesMeStateOptionProvider sop, final @Reference TimeZoneProvider tzp) {
this.oAuthFactory = oAuthFactory;
public MercedesMeHandlerFactory(@Reference HttpClientFactory hcf, @Reference StorageService storageService,
final @Reference LocaleProvider lp, final @Reference LocationProvider locationP,
final @Reference TimeZoneProvider tzp, final @Reference MercedesMeCommandOptionProvider cop,
final @Reference MercedesMeStateOptionProvider sop, final @Reference UnitProvider up,
final @Reference MetadataRegistry mdr, final @Reference ItemChannelLinkRegistry iclr,
final @Reference NetworkAddressService nas) {
this.storageService = storageService;
networkService = nas;
localeProvider = lp;
locationProvider = locationP;
mmcop = cop;
mmsop = sop;
timeZoneProvider = tzp;
httpClient = hcf.createHttpClient(Constants.BINDING_ID);
// https://github.com/jetty-project/jetty-reactive-httpclient/issues/33
httpClient.getProtocolHandlers().remove(WWWAuthenticationProtocolHandler.NAME);
try {
httpClient.start();
} catch (Exception e) {
logger.warn("HTTP client not started: {} - no web access possible!", e.getLocalizedMessage());
}
Utils.initialize(tzp, lp);
Mapper.initialize(up);
mdAdjuster = new MercedesMeMetadataAdjuster(mdr, iclr, up);
httpClient = hcf.getCommonHttpClient();
discoveryService = new MercedesMeDiscoveryService();
}
@Override
@ -85,21 +99,41 @@ public class MercedesMeHandlerFactory extends BaseThingHandlerFactory {
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
Bundle[] bundleList = this.getBundleContext().getBundles();
for (int i = 0; i < bundleList.length; i++) {
if ("org.openhab.binding.mercedesme".equals(bundleList[i].getSymbolicName())) {
ohVersion = bundleList[i].getVersion().toString();
}
}
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_ACCOUNT.equals(thingTypeUID)) {
return new AccountHandler((Bridge) thing, httpClient, oAuthFactory);
if (discoveryServiceReg == null) {
discoveryServiceReg = bundleContext.registerService(DiscoveryService.class.getName(), discoveryService,
null);
}
return new AccountHandler((Bridge) thing, discoveryService, httpClient, localeProvider, storageService,
networkService);
} else if (THING_TYPE_BEV.equals(thingTypeUID) || THING_TYPE_COMB.equals(thingTypeUID)
|| THING_TYPE_HYBRID.equals(thingTypeUID)) {
return new VehicleHandler(thing, locationProvider, mmcop, mmsop);
}
return new VehicleHandler(thing, httpClient, thingTypeUID.getId(), storageService, mmcop, mmsop,
timeZoneProvider);
return null;
}
@Override
protected void deactivate(ComponentContext componentContext) {
super.deactivate(componentContext);
try {
httpClient.stop();
} catch (Exception e) {
logger.debug("HTTP client not stopped: {}", e.getLocalizedMessage());
if (discoveryServiceReg != null) {
discoveryServiceReg.unregister();
discoveryServiceReg = null;
}
if (mdAdjuster != null) {
mdAdjuster = null;
}
}
public static String getVersion() {
return ohVersion;
}
}

View File

@ -0,0 +1,110 @@
/**
* 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.mercedesme.internal;
import javax.measure.Unit;
import javax.measure.quantity.Length;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.common.registry.RegistryChangeListener;
import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.items.Metadata;
import org.openhab.core.items.MetadataKey;
import org.openhab.core.items.MetadataRegistry;
import org.openhab.core.library.unit.ImperialUnits;
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.link.ItemChannelLink;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
/**
* {@link MercedesMeMetadataAdjuster} changes Metadata for channels not providing the system default unit
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class MercedesMeMetadataAdjuster implements RegistryChangeListener<ItemChannelLink> {
private final MetadataRegistry metadataRegistry;
private final ItemChannelLinkRegistry channelLinkRegistry;
private final UnitProvider unitProvider;
public MercedesMeMetadataAdjuster(MetadataRegistry mdr, ItemChannelLinkRegistry iclr, UnitProvider up) {
metadataRegistry = mdr;
channelLinkRegistry = iclr;
unitProvider = up;
channelLinkRegistry.addRegistryChangeListener(this);
}
/**
* Adjust Units to binding defaults
*/
@Override
public void added(ItemChannelLink element) {
ChannelUID cuid = element.getLinkedUID();
String itemName = element.getItemName();
if (Constants.BINDING_ID.equals(cuid.getBindingId())) {
MetadataKey key = new MetadataKey("unit", itemName);
switch (cuid.getId()) {
case Constants.GROUP_RANGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "mileage":
case Constants.GROUP_RANGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "range-electric":
case Constants.GROUP_RANGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "radius-electric":
case Constants.GROUP_RANGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "range-fuel":
case Constants.GROUP_RANGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "radius-fuel":
case Constants.GROUP_RANGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "range-hybrid":
case Constants.GROUP_RANGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "radius-hybrid":
case Constants.GROUP_RANGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "home-distance":
case Constants.GROUP_TRIP + ChannelUID.CHANNEL_GROUP_SEPARATOR + "distance":
case Constants.GROUP_TRIP + ChannelUID.CHANNEL_GROUP_SEPARATOR + "distance-reset":
if (metadataRegistry.get(key) == null) {
Unit<Length> lengthUnit = unitProvider.getUnit(Length.class);
if (ImperialUnits.FOOT.equals(lengthUnit)) {
metadataRegistry.add(new Metadata(key, ImperialUnits.MILE.getSymbol(), null));
} else if (SIUnits.METRE.equals(lengthUnit)) {
metadataRegistry.add(new Metadata(key, Constants.KILOMETRE_UNIT.toString(), null));
}
}
break;
case Constants.GROUP_RANGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "soc":
case Constants.GROUP_CHARGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "max-soc":
case Constants.GROUP_RANGE + ChannelUID.CHANNEL_GROUP_SEPARATOR + "fuel-level":
if (metadataRegistry.get(key) == null) {
metadataRegistry.add(new Metadata(key, Units.PERCENT.getSymbol(), null));
}
break;
case Constants.GROUP_TIRES + ChannelUID.CHANNEL_GROUP_SEPARATOR + "pressure-front-left":
case Constants.GROUP_TIRES + ChannelUID.CHANNEL_GROUP_SEPARATOR + "pressure-front-right":
case Constants.GROUP_TIRES + ChannelUID.CHANNEL_GROUP_SEPARATOR + "pressure-rear-left":
case Constants.GROUP_TIRES + ChannelUID.CHANNEL_GROUP_SEPARATOR + "pressure-rear-right":
if (metadataRegistry.get(key) == null) {
Unit<Length> lengthUnit = unitProvider.getUnit(Length.class);
if (ImperialUnits.FOOT.equals(lengthUnit)) {
metadataRegistry
.add(new Metadata(key, ImperialUnits.POUND_FORCE_SQUARE_INCH.getSymbol(), null));
} else if (SIUnits.METRE.equals(lengthUnit)) {
metadataRegistry.add(new Metadata(key, Units.BAR.getSymbol(), null));
}
}
break;
}
}
}
@Override
public void removed(ItemChannelLink element) {
}
@Override
public void updated(ItemChannelLink oldElement, ItemChannelLink element) {
}
}

View File

@ -0,0 +1,94 @@
/**
* 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.mercedesme.internal.actions;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONObject;
import org.openhab.binding.mercedesme.internal.handler.VehicleHandler;
import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.RuleAction;
import org.openhab.core.thing.binding.ThingActions;
import org.openhab.core.thing.binding.ThingActionsScope;
import org.openhab.core.thing.binding.ThingHandler;
/**
* {@link VehicleActions} which can be sent to a vehicle
*
* @author Bernd Weymann - Initial contribution
*/
@ThingActionsScope(name = "mercedesme")
@NonNullByDefault
public class VehicleActions implements ThingActions {
private Optional<VehicleHandler> thingHandler = Optional.empty();
private String[] argumentKey = new String[] { "city", "street", "postcode" };
@RuleAction(label = "@text/actionPoiLabel", description = "@text/actionPoiDescription")
/**
* Send Point of Interest (POI) to your vehicle.
* This POI is shown in your vehicle messages and can be instantly used to start a navigation route to this point.
* A "catchy" title plus latitude / longitude are mandatory.
* Parameters args is optional. If you use it respect the following order
* 1) City
* 2) Street
* 3) Postal Code
* If you miss any of them provide an empty String
*
* @param title - the title will be shown in your vehicle message inbox
* @param latitude - latitude of POI location
* @param longitude - longitude of POI location
* @param args - optional but respect order city, street, postal code
*/
public void sendPoi(
@ActionInput(name = "title", label = "@text/poiTitle", description = "@text/poiTitleDescription") String title,
@ActionInput(name = "latitude", label = "@text/latitudeLabel", description = "@text/latitudeDescription") double latitude,
@ActionInput(name = "longitude", label = "@text/longitudeLabel", description = "@text/longitudeDescription") double longitude,
String... args) {
if (thingHandler.isPresent()) {
JSONObject poi = new JSONObject();
poi.put("routeTitle", title);
poi.put("routeType", "singlePOI");
JSONArray waypoints = new JSONArray();
JSONObject waypoint = new JSONObject();
waypoint.put("title", title);
waypoint.put("latitude", latitude);
waypoint.put("longitude", longitude);
for (int i = 0; i < args.length; i++) {
waypoint.put(argumentKey[i], args[i]);
}
waypoints.put(waypoint);
poi.put("waypoints", waypoints);
thingHandler.get().sendPoi(poi);
}
}
public static void sendPoi(ThingActions actions, String title, double lat, double lon, String... args) {
((VehicleActions) actions).sendPoi(title, lat, lon, args);
}
@Override
public void setThingHandler(ThingHandler handler) {
thingHandler = Optional.of((VehicleHandler) handler);
}
@Override
public @Nullable ThingHandler getThingHandler() {
if (thingHandler.isPresent()) {
return thingHandler.get();
}
return null;
}
}

View File

@ -12,56 +12,23 @@
*/
package org.openhab.binding.mercedesme.internal.config;
import static org.openhab.binding.mercedesme.internal.Constants.*;
import static org.openhab.binding.mercedesme.internal.Constants.NOT_SET;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link AccountConfiguration} class contains fields mapping thing configuration parameters.
* {@link AccountConfiguration} for Account Bridge
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class AccountConfiguration {
public String clientId = NOT_SET;
public String clientSecret = NOT_SET;
public String imageApiKey = NOT_SET;
public String email = NOT_SET;
public String region = NOT_SET;
public String pin = NOT_SET;
public int refreshInterval = 15;
// Advanced Parameters
public String callbackIP = NOT_SET;
public int callbackPort = -1;
public boolean odoScope = true;
public boolean vehicleScope = true;
public boolean lockScope = true;
public boolean fuelScope = true;
public boolean evScope = true;
// https://developer.mercedes-benz.com/products/electric_vehicle_status/docs#_required_scopes
public String getScope() {
StringBuffer sb = new StringBuffer();
sb.append(SCOPE_OPENID).append(SPACE).append(SCOPE_OFFLINE);
if (odoScope) {
sb.append(SPACE).append(SCOPE_ODO);
}
if (vehicleScope) {
sb.append(SPACE).append(SCOPE_STATUS);
}
if (lockScope) {
sb.append(SPACE).append(SCOPE_LOCK);
}
if (fuelScope) {
sb.append(SPACE).append(SCOPE_FUEL);
}
if (evScope) {
sb.append(SPACE).append(SCOPE_EV);
}
return sb.toString();
}
@Override
public String toString() {
return "ID " + clientId + ", Secret " + clientSecret + ", IP " + callbackIP + ", Port " + callbackPort
+ ", scope " + getScope();
}
}

View File

@ -16,7 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.mercedesme.internal.Constants;
/**
* The {@link VehicleConfiguration} class contains fields mapping thing configuration parameters.
* {@link VehicleConfiguration} to configure vehicle
*
* @author Bernd Weymann - Initial contribution
*/
@ -24,14 +24,6 @@ import org.openhab.binding.mercedesme.internal.Constants;
public class VehicleConfiguration {
public String vin = Constants.NOT_SET;
public int refreshInterval = 5;
public float batteryCapacity = -1;
public float fuelCapacity = -1;
// Advanced
public boolean background = false;
public boolean night = false;
public boolean cropped = false;
public boolean roofOpen = false;
public String format = "webp";
}

View File

@ -0,0 +1,89 @@
/**
* 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.mercedesme.internal.discovery;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.mercedesme.internal.Constants;
import org.openhab.binding.mercedesme.internal.handler.AccountHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
/**
* {@link MercedesMeDiscoveryService} will be notified from Bridge {@link AccountHandler} regarding
* associated vehicles and provides DiscoveryResults
*
* @author Bernd Weymann - Initial Contribution
*/
@NonNullByDefault
@Component(service = DiscoveryService.class, configurationPid = "discovery.mercedesme")
public class MercedesMeDiscoveryService extends AbstractDiscoveryService {
public MercedesMeDiscoveryService() {
super(Constants.DISCOVERABLE_DEVICE_TYPE_UIDS, 0, false);
}
public void vehicleDiscovered(AccountHandler ac, String vin, Map<String, Object> properties) {
Object vehicleTypeObj = properties.get("vehicle");
String vehicleType = ((vehicleTypeObj == null) ? "unknown" : vehicleTypeObj.toString());
ThingTypeUID ttuid = null;
switch (vehicleType) {
case Constants.BEV:
ttuid = Constants.THING_TYPE_BEV;
break;
case Constants.COMBUSTION:
ttuid = Constants.THING_TYPE_COMB;
break;
case Constants.HYBRID:
ttuid = Constants.THING_TYPE_HYBRID;
break;
default:
break;
}
if (ttuid != null) {
thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(ttuid, ac.getThing().getUID(), vin))
.withBridge(ac.getThing().getUID()).withProperties(properties)
.withLabel("Mercedes Benz " + ttuid.getId().toUpperCase()).build());
}
}
public void vehicleRemove(AccountHandler ac, String vin, String vehicleType) {
ThingTypeUID ttuid = null;
switch (vehicleType) {
case Constants.BEV:
ttuid = Constants.THING_TYPE_BEV;
break;
case Constants.COMBUSTION:
ttuid = Constants.THING_TYPE_COMB;
break;
case Constants.HYBRID:
ttuid = Constants.THING_TYPE_HYBRID;
break;
default:
break;
}
if (ttuid != null) {
thingRemoved(new ThingUID(ttuid, ac.getThing().getUID(), vin));
}
}
@Override
protected void startScan() {
// not supported
}
}

View File

@ -0,0 +1,35 @@
/**
* 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.mercedesme.internal.dto;
import java.util.UUID;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link PINRequest} dto contains JSon body for PIN request
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class PINRequest {
public String emailOrPhoneNumber;
public String countryCode;
public String nonce;
public PINRequest(String mail, String country) {
emailOrPhoneNumber = mail;
countryCode = country;
nonce = UUID.randomUUID().toString();
}
}

View File

@ -0,0 +1,38 @@
/**
* 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.mercedesme.internal.dto;
import java.time.Instant;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.mercedesme.internal.Constants;
import com.google.gson.annotations.SerializedName;
/**
* The {@link TokenResponse} dto contains JSon body of token response
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class TokenResponse {
@SerializedName("access_token")
public String accessToken = Constants.NOT_SET;
@SerializedName("refresh_token")
public String refreshToken = Constants.NOT_SET;
@SerializedName("token_type")
public String tokenType = Constants.NOT_SET;
@SerializedName("expires_in")
public int expiresIn;
public String createdOn = Instant.now().toString();
}

View File

@ -12,19 +12,41 @@
*/
package org.openhab.binding.mercedesme.internal.handler;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.json.JSONArray;
import org.json.JSONObject;
import org.openhab.binding.mercedesme.internal.Constants;
import org.openhab.binding.mercedesme.internal.config.AccountConfiguration;
import org.openhab.binding.mercedesme.internal.server.CallbackServer;
import org.openhab.binding.mercedesme.internal.server.Utils;
import org.openhab.binding.mercedesme.internal.discovery.MercedesMeDiscoveryService;
import org.openhab.binding.mercedesme.internal.server.AuthServer;
import org.openhab.binding.mercedesme.internal.server.AuthService;
import org.openhab.binding.mercedesme.internal.server.MBWebsocket;
import org.openhab.binding.mercedesme.internal.utils.Utils;
import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.net.NetworkAddressService;
import org.openhab.core.storage.Storage;
import org.openhab.core.storage.StorageService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
@ -34,33 +56,61 @@ import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daimler.mbcarkit.proto.Client.ClientMessage;
import com.daimler.mbcarkit.proto.VehicleEvents.VEPUpdate;
import com.daimler.mbcarkit.proto.Vehicleapi.AppTwinCommandStatusUpdatesByPID;
/**
* The {@link AccountHandler} takes care of the valid authorization for the user account
* The {@link AccountHandler} acts as Bridge between MercedesMe Account and the associated vehicles
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class AccountHandler extends BaseBridgeHandler implements AccessTokenRefreshListener {
private static final String FEATURE_APPENDIX = "-features";
private static final String COMMAND_APPENDIX = "-commands";
private final Logger logger = LoggerFactory.getLogger(AccountHandler.class);
private final OAuthFactory oAuthFactory;
private final NetworkAddressService networkService;
private final MercedesMeDiscoveryService discoveryService;
private final HttpClient httpClient;
private Optional<CallbackServer> server = Optional.empty();
private final LocaleProvider localeProvider;
private final Storage<String> storage;
private final Map<String, VehicleHandler> activeVehicleHandlerMap = new HashMap<>();
private final Map<String, VEPUpdate> vepUpdateMap = new HashMap<>();
private final Map<String, Map<String, Object>> capabilitiesMap = new HashMap<>();
private Optional<AuthServer> server = Optional.empty();
private Optional<AuthService> authService = Optional.empty();
private Optional<ScheduledFuture<?>> scheduledFuture = Optional.empty();
private String capabilitiesEndpoint = "/v1/vehicle/%s/capabilities";
private String commandCapabilitiesEndpoint = "/v1/vehicle/%s/capabilities/commands";
private String poiEndpoint = "/v1/vehicle/%s/route";
final MBWebsocket ws;
Optional<AccountConfiguration> config = Optional.empty();
@Nullable
ClientMessage message;
public AccountHandler(Bridge bridge, HttpClient hc, OAuthFactory oaf) {
public AccountHandler(Bridge bridge, MercedesMeDiscoveryService mmds, HttpClient hc, LocaleProvider lp,
StorageService store, NetworkAddressService nas) {
super(bridge);
discoveryService = mmds;
networkService = nas;
ws = new MBWebsocket(this);
httpClient = hc;
oAuthFactory = oaf;
localeProvider = lp;
storage = store.getStorage(Constants.BINDING_ID);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// no commands available
}
@Override
public void initialize() {
updateStatus(ThingStatus.UNKNOWN);
config = Optional.of(getConfigAs(AccountConfiguration.class));
autodetectCallback();
String configValidReason = configValid();
@ -69,18 +119,40 @@ public class AccountHandler extends BaseBridgeHandler implements AccessTokenRefr
} else {
String callbackUrl = Utils.getCallbackAddress(config.get().callbackIP, config.get().callbackPort);
thing.setProperty("callbackUrl", callbackUrl);
server = Optional.of(new CallbackServer(this, httpClient, oAuthFactory, config.get(), callbackUrl));
server = Optional.of(new AuthServer(httpClient, config.get(), callbackUrl));
authService = Optional
.of(new AuthService(this, httpClient, config.get(), localeProvider.getLocale(), storage));
if (!server.get().start()) {
String textKey = Constants.STATUS_TEXT_PREFIX + thing.getThingTypeUID().getId()
+ Constants.STATUS_SERVER_RESTART;
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, textKey);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
textKey + " [\"" + thing.getProperties().get("callbackUrl") + "\"]");
} else {
// get fresh token
this.getToken();
scheduledFuture = Optional.of(scheduler.scheduleWithFixedDelay(this::update, 0,
config.get().refreshInterval, TimeUnit.MINUTES));
}
}
}
public void update() {
if (server.isPresent()) {
if (!Constants.NOT_SET.equals(authService.get().getToken())) {
ws.run();
} else {
// all failed - start manual authorization
String textKey = Constants.STATUS_TEXT_PREFIX + thing.getThingTypeUID().getId()
+ Constants.STATUS_AUTH_NEEDED;
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
textKey + " [\"" + thing.getProperties().get("callbackUrl") + "\"]");
}
} else {
// server not running - fix first
String textKey = Constants.STATUS_TEXT_PREFIX + thing.getThingTypeUID().getId()
+ Constants.STATUS_SERVER_RESTART;
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, textKey);
}
}
private void autodetectCallback() {
// if Callback IP and Callback Port are not set => autodetect these values
config = Optional.of(getConfigAs(AccountConfiguration.class));
@ -91,12 +163,12 @@ public class AccountHandler extends BaseBridgeHandler implements AccessTokenRefr
Utils.addPort(config.get().callbackPort);
}
if (!updateConfig.containsKey("callbackIP")) {
String ip;
try {
ip = Utils.getCallbackIP();
String ip = networkService.getPrimaryIpv4HostAddress();
if (ip != null) {
updateConfig.put("callbackIP", ip);
} catch (SocketException e) {
logger.info("Cannot detect IP address {}", e.getMessage());
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
"@text/mercedesme.account.status.ip-autodetect-failure");
}
}
super.updateConfiguration(updateConfig);
@ -107,14 +179,16 @@ public class AccountHandler extends BaseBridgeHandler implements AccessTokenRefr
private String configValid() {
config = Optional.of(getConfigAs(AccountConfiguration.class));
String textKey = Constants.STATUS_TEXT_PREFIX + thing.getThingTypeUID().getId();
if (config.get().callbackIP.equals(Constants.NOT_SET)) {
if (Constants.NOT_SET.equals(config.get().callbackIP)) {
return textKey + Constants.STATUS_IP_MISSING;
} else if (config.get().callbackPort == -1) {
return textKey + Constants.STATUS_PORT_MISSING;
} else if (config.get().clientId.equals(Constants.NOT_SET)) {
return textKey + Constants.STATUS_CLIENT_ID_MISSING;
} else if (config.get().clientSecret.equals(Constants.NOT_SET)) {
return textKey + Constants.STATUS_CLIENT_SECRET_MISSING;
} else if (Constants.NOT_SET.equals(config.get().email)) {
return textKey + Constants.STATUS_EMAIL_MISSING;
} else if (Constants.NOT_SET.equals(config.get().region)) {
return textKey + Constants.STATUS_REGION_MISSING;
} else if (config.get().refreshInterval <= 01) {
return textKey + Constants.STATUS_REFRESH_INVALID;
} else {
return Constants.EMPTY;
}
@ -123,18 +197,18 @@ public class AccountHandler extends BaseBridgeHandler implements AccessTokenRefr
@Override
public void dispose() {
if (server.isPresent()) {
CallbackServer serv = server.get();
serv.stop();
serv.dispose();
AuthServer authServer = server.get();
authServer.stop();
authServer.dispose();
server = Optional.empty();
Utils.removePort(config.get().callbackPort);
}
}
@Override
public void handleRemoval() {
server.ifPresent(s -> s.deleteOAuthServiceAndAccessToken());
super.handleRemoval();
ws.interrupt();
scheduledFuture.ifPresent(schedule -> {
if (!schedule.isCancelled()) {
schedule.cancel(true);
}
});
}
/**
@ -142,9 +216,8 @@ public class AccountHandler extends BaseBridgeHandler implements AccessTokenRefr
*/
@Override
public void onAccessTokenResponse(AccessTokenResponse tokenResponse) {
if (!tokenResponse.getAccessToken().isEmpty()) {
// token not empty - fine
updateStatus(ThingStatus.ONLINE);
if (!Constants.NOT_SET.equals(tokenResponse.getAccessToken())) {
scheduler.schedule(this::update, 2, TimeUnit.SECONDS);
} else if (server.isEmpty()) {
// server not running - fix first
String textKey = Constants.STATUS_TEXT_PREFIX + thing.getThingTypeUID().getId()
@ -159,16 +232,240 @@ public class AccountHandler extends BaseBridgeHandler implements AccessTokenRefr
}
}
public String getToken() {
return server.get().getToken();
}
public String getImageApiKey() {
return config.get().imageApiKey;
}
@Override
public String toString() {
return Integer.toString(config.get().callbackPort);
}
public String getWSUri() {
return Utils.getWebsocketServer(config.get().region);
}
public ClientUpgradeRequest getClientUpgradeRequest() {
ClientUpgradeRequest request = new ClientUpgradeRequest();
request.setHeader("Authorization", authService.get().getToken());
request.setHeader("X-SessionId", UUID.randomUUID().toString());
request.setHeader("X-TrackingId", UUID.randomUUID().toString());
request.setHeader("Ris-Os-Name", Constants.RIS_OS_NAME);
request.setHeader("Ris-Os-Version", Constants.RIS_OS_VERSION);
request.setHeader("Ris-Sdk-Version", Utils.getRisSDKVersion(config.get().region));
request.setHeader("X-Locale",
localeProvider.getLocale().getLanguage() + "-" + localeProvider.getLocale().getCountry()); // de-DE
request.setHeader("User-Agent", Utils.getApplication(config.get().region));
request.setHeader("X-Applicationname", Utils.getUserAgent(config.get().region));
request.setHeader("Ris-Application-Version", Utils.getRisApplicationVersion(config.get().region));
return request;
}
public void registerVin(String vin, VehicleHandler handler) {
discoveryService.vehicleRemove(this, vin, handler.getThing().getThingTypeUID().getId());
activeVehicleHandlerMap.put(vin, handler);
VEPUpdate updateForVin = vepUpdateMap.get(vin);
if (updateForVin != null) {
handler.distributeContent(updateForVin);
}
}
public void unregisterVin(String vin) {
activeVehicleHandlerMap.remove(vin);
}
@SuppressWarnings("null")
public void getVehicleCapabilities(String vin) {
if (storage.containsKey(vin + FEATURE_APPENDIX)) {
if (activeVehicleHandlerMap.containsKey(vin)) {
activeVehicleHandlerMap.get(vin).setFeatureCapabilities(storage.get(vin + FEATURE_APPENDIX));
}
}
if (storage.containsKey(vin + COMMAND_APPENDIX)) {
if (activeVehicleHandlerMap.containsKey(vin)) {
activeVehicleHandlerMap.get(vin).setCommandCapabilities(storage.get(vin + COMMAND_APPENDIX));
}
}
}
public boolean distributeVepUpdates(Map<String, VEPUpdate> map) {
List<String> notFoundList = new ArrayList<>();
map.forEach((key, value) -> {
VehicleHandler h = activeVehicleHandlerMap.get(key);
if (h != null) {
h.distributeContent(value);
} else {
if (value.getFullUpdate()) {
vepUpdateMap.put(key, value);
}
notFoundList.add(key);
}
});
notFoundList.forEach(vin -> {
logger.trace("No VehicleHandler available for VIN {}", vin);
});
return notFoundList.isEmpty();
}
public void commandStatusUpdate(Map<String, AppTwinCommandStatusUpdatesByPID> updatesByVinMap) {
updatesByVinMap.forEach((key, value) -> {
VehicleHandler h = activeVehicleHandlerMap.get(key);
if (h != null) {
h.distributeCommandStatus(value);
} else {
logger.trace("No VehicleHandler available for VIN {}", key);
}
});
}
@SuppressWarnings("null")
public void discovery(String vin) {
if (activeVehicleHandlerMap.containsKey(vin)) {
VehicleHandler vh = activeVehicleHandlerMap.get(vin);
if (vh.getThing().getProperties().isEmpty()) {
vh.getThing().setProperties(getStringCapabilities(vin));
}
} else {
if (!capabilitiesMap.containsKey(vin)) {
// only report new discovery if capabilities aren't discovered yet
discoveryService.vehicleDiscovered(this, vin, getCapabilities(vin));
}
}
}
private Map<String, String> getStringCapabilities(String vin) {
Map<String, Object> props = getCapabilities(vin);
Map<String, String> stringProps = new HashMap<>();
props.forEach((key, value) -> {
stringProps.put(key, value.toString());
});
return stringProps;
}
private Map<String, Object> getCapabilities(String vin) {
// check cache before hammering API
Map<String, Object> m = capabilitiesMap.get(vin);
if (m != null) {
return m;
}
Map<String, Object> featureMap = new HashMap<>();
try {
// add vehicle capabilities
String capabilitiesUrl = Utils.getRestAPIServer(config.get().region)
+ String.format(capabilitiesEndpoint, vin);
Request capabilitiesRequest = httpClient.newRequest(capabilitiesUrl);
authService.get().addBasicHeaders(capabilitiesRequest);
capabilitiesRequest.header("X-SessionId", UUID.randomUUID().toString());
capabilitiesRequest.header("X-TrackingId", UUID.randomUUID().toString());
capabilitiesRequest.header("Authorization", authService.get().getToken());
ContentResponse capabilitiesResponse = capabilitiesRequest
.timeout(Constants.REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS).send();
String featureCapabilitiesJsonString = capabilitiesResponse.getContentAsString();
if (!storage.containsKey(vin + FEATURE_APPENDIX)) {
storage.put(vin + FEATURE_APPENDIX, featureCapabilitiesJsonString);
}
JSONObject jsonResponse = new JSONObject(featureCapabilitiesJsonString);
JSONObject features = jsonResponse.getJSONObject("features");
features.keySet().forEach(key -> {
String value = features.get(key).toString();
String newKey = Character.toUpperCase(key.charAt(0)) + key.substring(1);
newKey = "feature" + newKey;
featureMap.put(newKey, value);
});
// get vehicle type
JSONObject vehicle = jsonResponse.getJSONObject("vehicle");
JSONArray fuelTypes = vehicle.getJSONArray("fuelTypes");
if (fuelTypes.length() > 1) {
featureMap.put("vehicle", Constants.HYBRID);
} else if ("ELECTRIC".equals(fuelTypes.get(0))) {
featureMap.put("vehicle", Constants.BEV);
} else {
featureMap.put("vehicle", Constants.COMBUSTION);
}
// add command capabilities
String commandCapabilitiesUrl = Utils.getRestAPIServer(config.get().region)
+ String.format(commandCapabilitiesEndpoint, vin);
Request commandCapabilitiesRequest = httpClient.newRequest(commandCapabilitiesUrl);
authService.get().addBasicHeaders(commandCapabilitiesRequest);
commandCapabilitiesRequest.header("X-SessionId", UUID.randomUUID().toString());
commandCapabilitiesRequest.header("X-TrackingId", UUID.randomUUID().toString());
commandCapabilitiesRequest.header("Authorization", authService.get().getToken());
ContentResponse commandCapabilitiesResponse = commandCapabilitiesRequest
.timeout(Constants.REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS).send();
String commandCapabilitiesJsonString = commandCapabilitiesResponse.getContentAsString();
if (!storage.containsKey(vin + COMMAND_APPENDIX)) {
storage.put(vin + COMMAND_APPENDIX, commandCapabilitiesJsonString);
}
JSONObject commands = new JSONObject(commandCapabilitiesJsonString);
JSONArray commandArray = commands.getJSONArray("commands");
commandArray.forEach(object -> {
String commandName = ((JSONObject) object).get("commandName").toString();
String[] words = commandName.split("[\\W_]+");
StringBuilder builder = new StringBuilder();
builder.append("command");
for (int i = 0; i < words.length; i++) {
String word = words[i];
word = word.isEmpty() ? word
: Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase();
builder.append(word);
}
String value = ((JSONObject) object).get("isAvailable").toString();
featureMap.put(builder.toString(), value);
});
// store in cache
capabilitiesMap.put(vin, featureMap);
return featureMap;
} catch (InterruptedException | TimeoutException | ExecutionException e) {
logger.trace("Error retrieving capabilities: {}", e.getMessage());
featureMap.clear();
}
return featureMap;
}
public void sendCommand(@Nullable ClientMessage cm) {
if (cm != null) {
ws.setCommand(cm);
}
scheduler.schedule(this::update, 2, TimeUnit.SECONDS);
}
public void keepAlive(boolean b) {
ws.keepAlive(b);
}
@Override
public void updateStatus(ThingStatus ts) {
super.updateStatus(ts);
}
@Override
public void updateStatus(ThingStatus ts, ThingStatusDetail tsd, @Nullable String tsdt) {
super.updateStatus(ts, tsd, tsdt);
}
/**
* Vehicle Actions
*
* @param poi
*/
public void sendPoi(String vin, JSONObject poi) {
String poiUrl = Utils.getRestAPIServer(config.get().region) + String.format(poiEndpoint, vin);
Request poiRequest = httpClient.POST(poiUrl);
authService.get().addBasicHeaders(poiRequest);
poiRequest.header("X-SessionId", UUID.randomUUID().toString());
poiRequest.header("X-TrackingId", UUID.randomUUID().toString());
poiRequest.header("Authorization", authService.get().getToken());
poiRequest.header(HttpHeader.CONTENT_TYPE, "application/json");
poiRequest.content(new StringContentProvider(poi.toString(), "utf-8"));
try {
ContentResponse cr = poiRequest.timeout(Constants.REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS).send();
logger.trace("Send POI Response {} : {}", cr.getStatus(), cr.getContentAsString());
} catch (InterruptedException | TimeoutException | ExecutionException e) {
logger.trace("Error Sending POI {}", e.getMessage());
}
}
}

View File

@ -0,0 +1,106 @@
/**
* 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.mercedesme.internal.server;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletHandler;
import org.openhab.binding.mercedesme.internal.Constants;
import org.openhab.binding.mercedesme.internal.config.AccountConfiguration;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link AuthServer} provides HTTP Server to show servlet content of the authentication process
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class AuthServer {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthServer.class);
private static final Map<Integer, AuthServer> SERVER_MAP = new HashMap<>();
private static final AccessTokenResponse INVALID_ACCESS_TOKEN = new AccessTokenResponse();
private final HttpClient httpClient;
private Optional<Server> server = Optional.empty();
private AccountConfiguration config;
public String callbackUrl;
public AuthServer(HttpClient hc, AccountConfiguration config, String callbackUrl) {
httpClient = hc;
SERVER_MAP.put(Integer.valueOf(config.callbackPort), this);
this.config = config;
this.callbackUrl = callbackUrl;
INVALID_ACCESS_TOKEN.setAccessToken(Constants.EMPTY);
}
public void dispose() {
SERVER_MAP.remove(Integer.valueOf(config.callbackPort));
}
public boolean start() {
// avoid real server start for unit tests
if (server.isPresent() || Constants.JUNIT_SERVER_ADDR.equals(callbackUrl)) {
return true;
}
server = Optional.of(new Server());
ServerConnector connector = new ServerConnector(server.get());
connector.setPort(config.callbackPort);
server.get().setConnectors(new Connector[] { connector });
ServletHandler servletHandler = new ServletHandler();
server.get().setHandler(servletHandler);
servletHandler.addServletWithMapping(AuthServlet.class, Constants.CALLBACK_ENDPOINT);
try {
server.get().start();
return true;
} catch (Exception e) {
LOGGER.trace("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
server = Optional.empty();
return false;
}
}
public void stop() {
try {
if (server.isPresent()) {
server.get().stop();
server = Optional.empty();
}
} catch (Exception e) {
LOGGER.trace("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
}
}
@Nullable
public static AuthServer getServer(int port) {
return SERVER_MAP.get(port);
}
public HttpClient getHttpClient() {
return httpClient;
}
public String getRegion() {
return config.region;
}
}

View File

@ -0,0 +1,274 @@
/**
* 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.mercedesme.internal.server;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.openhab.binding.mercedesme.internal.Constants;
import org.openhab.binding.mercedesme.internal.config.AccountConfiguration;
import org.openhab.binding.mercedesme.internal.dto.PINRequest;
import org.openhab.binding.mercedesme.internal.dto.TokenResponse;
import org.openhab.binding.mercedesme.internal.utils.Utils;
import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.storage.Storage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link AuthService} helpers for token management
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class AuthService {
public static final AccessTokenResponse INVALID_TOKEN = new AccessTokenResponse();
private static final int EXPIRATION_BUFFER = 5;
private static final Map<Integer, AuthService> AUTH_MAP = new HashMap<>();
private final Logger logger = LoggerFactory.getLogger(AuthService.class);
AccessTokenRefreshListener listener;
private HttpClient httpClient;
private String identifier;
private AccountConfiguration config;
private Locale locale;
private Storage<String> storage;
private AccessTokenResponse token;
public AuthService(AccessTokenRefreshListener atrl, HttpClient hc, AccountConfiguration ac, Locale l,
Storage<String> store) {
INVALID_TOKEN.setAccessToken(Constants.NOT_SET);
INVALID_TOKEN.setRefreshToken(Constants.NOT_SET);
listener = atrl;
httpClient = hc;
config = ac;
identifier = config.email;
locale = l;
storage = store;
// restore token
String storedObject = storage.get(identifier);
if (storedObject == null) {
token = INVALID_TOKEN;
listener.onAccessTokenResponse(token);
} else {
token = Utils.fromString(storedObject);
if (token.isExpired(Instant.now(), EXPIRATION_BUFFER)) {
if (!Constants.NOT_SET.equals(token.getRefreshToken())) {
refreshToken();
listener.onAccessTokenResponse(token);
} else {
token = INVALID_TOKEN;
listener.onAccessTokenResponse(token);
}
} else {
listener.onAccessTokenResponse(token);
}
}
AUTH_MAP.put(config.callbackPort, this);
}
@Nullable
public static AuthService getAuthService(Integer key) {
return AUTH_MAP.get(key);
}
/**
*
* @return guid from request to create token in next step
*/
public String requestPin() {
String configUrl = Utils.getAuthConfigURL(config.region);
String sessionId = UUID.randomUUID().toString();
Request configRequest = httpClient.newRequest(configUrl);
addBasicHeaders(configRequest);
configRequest.header("X-Trackingid", UUID.randomUUID().toString());
configRequest.header("X-Sessionid", sessionId);
try {
ContentResponse cr = configRequest.timeout(Constants.REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS).send();
if (cr.getStatus() == 200) {
logger.trace("{} Config Request PIN fine {} {}", prefix(), cr.getStatus(), cr.getContentAsString());
} else {
logger.trace("{} Failed to request config for pin {} {}", prefix(), cr.getStatus(),
cr.getContentAsString());
return Constants.NOT_SET;
}
} catch (InterruptedException | TimeoutException | ExecutionException e) {
logger.trace("{} Failed to request config for pin {}", prefix(), e.getMessage());
return Constants.NOT_SET;
}
String url = Utils.getAuthURL(config.region);
Request req = httpClient.POST(url);
addBasicHeaders(req);
req.header("X-Trackingid", UUID.randomUUID().toString());
req.header("X-Sessionid", sessionId);
PINRequest pr = new PINRequest(config.email, locale.getCountry());
req.header(HttpHeader.CONTENT_TYPE, "application/json");
logger.trace("{} payload {}", url, Utils.GSON.toJson(pr));
req.content(new StringContentProvider(Utils.GSON.toJson(pr), "utf-8"));
try {
ContentResponse cr = req.timeout(Constants.REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS).send();
if (cr.getStatus() == 200) {
logger.trace("{} Request PIN fine {} {}", prefix(), cr.getStatus(), cr.getContentAsString());
return pr.nonce;
} else {
logger.trace("{} Failed to request pin {} {}", prefix(), cr.getStatus(), cr.getContentAsString());
}
} catch (InterruptedException | TimeoutException | ExecutionException e) {
logger.trace("{} Failed to request pin {}", prefix(), e.getMessage());
}
return Constants.NOT_SET;
}
public boolean requestToken(String password) {
try {
// Request + headers
String url = Utils.getTokenUrl(config.region);
Request req = httpClient.POST(url);
addBasicHeaders(req);
req.header("Stage", "prod");
req.header("X-Device-Id", UUID.randomUUID().toString());
req.header("X-Request-Id", UUID.randomUUID().toString());
// Content URL form
String clientId = "client_id="
+ URLEncoder.encode(Utils.getLoginAppId(config.region), StandardCharsets.UTF_8.toString());
String grantAttribute = "grant_type=password";
String userAttribute = "username=" + URLEncoder.encode(config.email, StandardCharsets.UTF_8.toString());
String passwordAttribute = "password=" + URLEncoder.encode(password, StandardCharsets.UTF_8.toString());
String scopeAttribute = "scope=" + URLEncoder.encode(Constants.SCOPE, StandardCharsets.UTF_8.toString());
String content = clientId + "&" + grantAttribute + "&" + userAttribute + "&" + passwordAttribute + "&"
+ scopeAttribute;
req.header(HttpHeader.CONTENT_TYPE, "application/x-www-form-urlencoded");
req.content(new StringContentProvider(content));
// Send
ContentResponse cr = req.timeout(Constants.REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS).send();
if (cr.getStatus() == 200) {
String responseString = cr.getContentAsString();
saveTokenResponse(responseString);
listener.onAccessTokenResponse(token);
return true;
} else {
logger.trace("{} Failed to get token {} {}", prefix(), cr.getStatus(), cr.getContentAsString());
}
} catch (InterruptedException | TimeoutException | ExecutionException | UnsupportedEncodingException e) {
logger.trace("{} Failed to get token {}", prefix(), e.getMessage());
}
return false;
}
public void refreshToken() {
try {
String url = Utils.getTokenUrl(config.region);
Request req = httpClient.POST(url);
req.header("X-Device-Id", UUID.randomUUID().toString());
req.header("X-Request-Id", UUID.randomUUID().toString());
// Content URL form
String grantAttribute = "grant_type=refresh_token";
String refreshTokenAttribute = "refresh_token="
+ URLEncoder.encode(token.getRefreshToken(), StandardCharsets.UTF_8.toString());
String content = grantAttribute + "&" + refreshTokenAttribute;
req.header(HttpHeader.CONTENT_TYPE, "application/x-www-form-urlencoded");
req.content(new StringContentProvider(content));
// Send
ContentResponse cr = req.timeout(Constants.REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS).send();
if (cr.getStatus() == 200) {
saveTokenResponse(cr.getContentAsString());
listener.onAccessTokenResponse(token);
} else {
logger.trace("{} Failed to refresh token {} {}", prefix(), cr.getStatus(), cr.getContentAsString());
}
} catch (InterruptedException | TimeoutException | ExecutionException | UnsupportedEncodingException e) {
logger.trace("{} Failed to refresh token {}", prefix(), e.getMessage());
}
}
public String getToken() {
if (token.isExpired(Instant.now(), EXPIRATION_BUFFER)) {
if (!Constants.NOT_SET.equals(token.getRefreshToken())) {
refreshToken();
// token shall be updated now - retry expired check
if (token.isExpired(Instant.now(), EXPIRATION_BUFFER)) {
token = INVALID_TOKEN;
listener.onAccessTokenResponse(token);
return Constants.NOT_SET;
}
} else {
token = INVALID_TOKEN;
logger.trace("{} Refresh token empty", prefix());
}
}
return token.getAccessToken();
}
public void addBasicHeaders(Request req) {
req.header("Ris-Os-Name", Constants.RIS_OS_NAME);
req.header("Ris-Os-Version", Constants.RIS_OS_VERSION);
req.header("Ris-Sdk-Version", Utils.getRisSDKVersion(config.region));
req.header("X-Locale", locale.getLanguage() + "-" + locale.getCountry()); // de-DE
req.header("User-Agent", Utils.getApplication(config.region));
req.header("X-Applicationname", Utils.getUserAgent(config.region));
req.header("Ris-Application-Version", Utils.getRisApplicationVersion(config.region));
}
private void saveTokenResponse(String response) {
TokenResponse tr = Utils.GSON.fromJson(response, TokenResponse.class);
AccessTokenResponse atr = new AccessTokenResponse();
if (tr != null) {
atr.setAccessToken(tr.accessToken);
atr.setCreatedOn(Instant.now());
atr.setExpiresIn(tr.expiresIn);
// Preserve refresh token if available
if (Constants.NOT_SET.equals(tr.refreshToken) && !Constants.NOT_SET.equals(token.getRefreshToken())) {
atr.setRefreshToken(token.getRefreshToken());
} else if (!Constants.NOT_SET.equals(tr.refreshToken)) {
atr.setRefreshToken(tr.refreshToken);
} else {
logger.trace("{} Neither new nor old refresh token available", prefix());
}
atr.setTokenType("Bearer");
atr.setScope(Constants.SCOPE);
storage.put(identifier, Utils.toString(atr));
token = atr;
} else {
logger.trace("{} Token Response is null", prefix());
}
}
private String prefix() {
return "[" + config.email + "] ";
}
}

View File

@ -0,0 +1,104 @@
/**
* 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.mercedesme.internal.server;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.mercedesme.internal.Constants;
/**
* {@link AuthServlet} provides simple HTML pages for authorization workflow
*
* @author Bernd Weymann - Initial contribution
*/
@SuppressWarnings("serial")
@NonNullByDefault
public class AuthServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
AuthService myAuthService = AuthService.getAuthService(request.getLocalPort());
String guid = request.getParameter(Constants.GUID);
String pin = request.getParameter(Constants.PIN);
if (guid == null && pin == null && myAuthService != null) {
// request PIN
String requestVal = myAuthService.requestPin();
if (!Constants.NOT_SET.equals(requestVal)) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("<HTML>");
response.getWriter().println("<BODY>");
response.getWriter().println("<H1>Step 1 - PIN Requested</H1>");
response.getWriter().println("<BR>");
response.getWriter().println("PIN was requested and should be present in your EMail Inbox<BR>");
response.getWriter()
.println("Check first if you received the PIN and then continue with the below Link<BR>");
response.getWriter().println("<a href=\"" + Constants.CALLBACK_ENDPOINT + "?guid=" + requestVal
+ "\">Click here to continue with Step 2</a>");
response.getWriter().println("</BODY>");
response.getWriter().println("</HTML>");
} else {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("<HTML>");
response.getWriter().println("<BODY>");
response.getWriter().println("Something went wrong<BR>");
response.getWriter().println("</BODY>");
response.getWriter().println("</HTML>");
}
} else if (guid != null && pin == null && myAuthService != null) {
// show insert PIN input field
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("<HTML>");
response.getWriter().println("<BODY>");
response.getWriter().println("<H1>Step 2 - Enter PIN</H1>");
response.getWriter().println("<BR>");
response.getWriter().println("Enter PIN in second input field - leave guid as it is!<BR>");
response.getWriter().println("<form action=\"" + Constants.CALLBACK_ENDPOINT + "\">");
response.getWriter().println("<BR>");
response.getWriter().println("<label for=\"GUID\">GUID</label>");
response.getWriter().println("<input type=\"text\" id=\"guid\" name=\"guid\" value=\"" + guid + "\">");
response.getWriter().println("<BR>");
response.getWriter().println("<label for=\"PIN\">PIN</label>");
response.getWriter().println("<input type=\"text\" id=\"pin\" name=\"pin\" placeholder=\"Your PIN\">");
response.getWriter().println("<BR>");
response.getWriter().println("<input type=\"submit\" value=\"Submit\">");
response.getWriter().println("</form>");
response.getWriter().println("</BODY>");
response.getWriter().println("</HTML>");
} else if (guid != null && pin != null && myAuthService != null) {
// call getToken and show result
boolean result = myAuthService.requestToken(guid + ":" + pin);
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("<HTML>");
response.getWriter().println("<BODY>");
response.getWriter().println("<H1>Step 3 - Save Token</H1>");
response.getWriter().println("<BR>");
if (result) {
response.getWriter().println("Success - everything done!<BR>");
} else {
response.getWriter().println("Failure - Please check logs for further analysis!<BR>");
}
response.getWriter().println("</BODY>");
response.getWriter().println("</HTML>");
}
}
}

View File

@ -1,196 +0,0 @@
/**
* 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.mercedesme.internal.server;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletHandler;
import org.openhab.binding.mercedesme.internal.Constants;
import org.openhab.binding.mercedesme.internal.config.AccountConfiguration;
import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthException;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link CallbackServer} class defines an HTTP Server for authentication callbacks
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class CallbackServer {
private static final Logger LOGGER = LoggerFactory.getLogger(CallbackServer.class);
private static final Map<Integer, OAuthClientService> AUTH_MAP = new HashMap<>();
private static final Map<Integer, CallbackServer> SERVER_MAP = new HashMap<>();
private static final AccessTokenResponse INVALID_ACCESS_TOKEN = new AccessTokenResponse();
private final OAuthFactory oAuthFactory;
private Optional<Server> server = Optional.empty();
private AccessTokenRefreshListener listener;
private AccountConfiguration config;
private OAuthClientService oacs;
private String callbackUrl;
public CallbackServer(AccessTokenRefreshListener l, HttpClient hc, OAuthFactory oAuthFactory,
AccountConfiguration config, String callbackUrl) {
this.oAuthFactory = oAuthFactory;
oacs = oAuthFactory.createOAuthClientService(config.clientId, Constants.MB_TOKEN_URL, Constants.MB_AUTH_URL,
config.clientId, config.clientSecret, config.getScope(), false);
listener = l;
AUTH_MAP.put(Integer.valueOf(config.callbackPort), oacs);
SERVER_MAP.put(Integer.valueOf(config.callbackPort), this);
this.config = config;
this.callbackUrl = callbackUrl;
INVALID_ACCESS_TOKEN.setAccessToken(Constants.EMPTY);
}
public void dispose() {
oAuthFactory.ungetOAuthService(config.clientId);
AUTH_MAP.remove(Integer.valueOf(config.callbackPort));
SERVER_MAP.remove(Integer.valueOf(config.callbackPort));
}
public void deleteOAuthServiceAndAccessToken() {
oAuthFactory.deleteServiceAndAccessToken(config.clientId);
}
public String getAuthorizationUrl() {
try {
return oacs.getAuthorizationUrl(callbackUrl, null, null);
} catch (OAuthException e) {
LOGGER.warn("Error creating Authorization URL {}", e.getMessage());
return Constants.EMPTY;
}
}
public String getScope() {
return config.getScope();
}
public boolean start() {
LOGGER.debug("Start Callback Server for port {}", config.callbackPort);
if (server.isPresent()) {
LOGGER.debug("Callback server for port {} already started", config.callbackPort);
return true;
}
server = Optional.of(new Server());
ServerConnector connector = new ServerConnector(server.get());
connector.setPort(config.callbackPort);
server.get().setConnectors(new Connector[] { connector });
ServletHandler servletHandler = new ServletHandler();
server.get().setHandler(servletHandler);
servletHandler.addServletWithMapping(CallbackServlet.class, Constants.CALLBACK_ENDPOINT);
try {
server.get().start();
} catch (Exception e) {
LOGGER.warn("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
return false;
}
return true;
}
public void stop() {
LOGGER.debug("Stop Callback Server");
try {
if (server.isPresent()) {
server.get().stop();
server = Optional.empty();
}
} catch (Exception e) {
LOGGER.warn("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
}
}
public String getToken() {
AccessTokenResponse atr = null;
try {
/*
* this will automatically trigger
* - return last stored token if it's still valid
* - refreshToken if current token is expired
* - inform listeners if refresh delivered new token
* - store new token in persistence
*/
atr = oacs.getAccessTokenResponse();
} catch (OAuthException | IOException | OAuthResponseException e) {
LOGGER.warn("Exception getting token {}", e.getMessage());
}
if (atr == null) {
LOGGER.debug("Token empty - Manual Authorization needed at {}", callbackUrl);
listener.onAccessTokenResponse(INVALID_ACCESS_TOKEN);
return INVALID_ACCESS_TOKEN.getAccessToken();
}
listener.onAccessTokenResponse(atr);
return atr.getAccessToken();
}
/**
* Static callback for Servlet calls
*
* @param port
* @param code
*/
public static void callback(int port, String code) {
LOGGER.trace("Callback from Servlet {} {}", port, code);
try {
OAuthClientService oacs = AUTH_MAP.get(port);
LOGGER.trace("Get token from code {}", code);
// get CallbackServer instance
CallbackServer srv = SERVER_MAP.get(port);
LOGGER.trace("Deliver token to {}", srv);
if (srv != null && oacs != null) {
// token stored and persisted inside oacs
AccessTokenResponse atr = oacs.getAccessTokenResponseByAuthorizationCode(code, srv.callbackUrl);
// inform listener - not done by oacs
srv.listener.onAccessTokenResponse(atr);
} else {
LOGGER.warn("Either Callbackserver {} or Authorization Service {} not found", srv, oacs);
}
} catch (OAuthException | IOException | OAuthResponseException e) {
LOGGER.warn("Exception getting token from code {} {}", code, e.getMessage());
}
}
public static String getAuthorizationUrl(int port) {
CallbackServer srv = SERVER_MAP.get(port);
if (srv != null) {
return srv.getAuthorizationUrl();
} else {
LOGGER.debug("No Callbackserver found for {}", port);
return Constants.EMPTY;
}
}
public static String getScope(int port) {
CallbackServer srv = SERVER_MAP.get(port);
if (srv != null) {
return srv.getScope();
} else {
LOGGER.debug("No Callbackserver found for {}", port);
return Constants.EMPTY;
}
}
}

View File

@ -1,73 +0,0 @@
/**
* 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.mercedesme.internal.server;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.mercedesme.internal.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link CallbackServlet} class provides authentication callback endpoint
*
* @author Bernd Weymann - Initial contribution
*/
@SuppressWarnings("serial")
@NonNullByDefault
public class CallbackServlet extends HttpServlet {
private final Logger logger = LoggerFactory.getLogger(CallbackServlet.class);
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String code = request.getParameter(Constants.CODE);
if (code != null) {
CallbackServer.callback(request.getLocalPort(), code);
logger.trace("Code successfully extracted {}", request.getParameterMap());
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(request.getParameterMap());
response.getWriter().println("{ \"status\": \"ok\"}");
} else {
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("<HTML>");
response.getWriter().println("<BODY>");
response.getWriter().println("<B>Call Parameters</B>");
response.getWriter().println("<BR>");
response.getWriter().println(request.getParameterMap());
response.getWriter().println("<BR><BR>");
response.getWriter().println("<B>Configured scopes</B><BR>");
String[] scopes = CallbackServer.getScope(request.getLocalPort()).split(Constants.SPACE);
for (int i = 0; i < scopes.length; i++) {
response.getWriter().println(scopes[i] + "<BR>");
}
response.getWriter().println("<BR><BR>");
response.getWriter().println("<B>Get your access token for openHAB MercedesMe Binding</B>");
response.getWriter().println("<BR>");
response.getWriter().println("<a href=\"" + CallbackServer.getAuthorizationUrl(request.getLocalPort())
+ "\">Start Authorization</a>");
response.getWriter().println("</BODY>");
response.getWriter().println("</HTML>");
}
logger.debug("Call from {}:{} parameters {}", request.getLocalAddr(), request.getLocalPort(),
request.getParameterMap());
}
}

View File

@ -0,0 +1,265 @@
/**
* 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.mercedesme.internal.server;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.binding.mercedesme.internal.Constants;
import org.openhab.binding.mercedesme.internal.handler.AccountHandler;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daimler.mbcarkit.proto.Client.ClientMessage;
import com.daimler.mbcarkit.proto.Protos.AcknowledgeAssignedVehicles;
import com.daimler.mbcarkit.proto.VehicleEvents;
import com.daimler.mbcarkit.proto.VehicleEvents.AcknowledgeVEPUpdatesByVIN;
import com.daimler.mbcarkit.proto.VehicleEvents.PushMessage;
import com.daimler.mbcarkit.proto.Vehicleapi.AcknowledgeAppTwinCommandStatusUpdatesByVIN;
import com.daimler.mbcarkit.proto.Vehicleapi.AppTwinCommandStatusUpdatesByVIN;
/**
* {@link MBWebsocket} as socket endpoint to communicate with Mercedes
*
* @author Bernd Weymann - Initial contribution
*/
@WebSocket
@NonNullByDefault
public class MBWebsocket {
// timeout 14 Minutes - just below scheduling of 15 Minutes by AccountHandler
private static final int CONNECT_TIMEOUT_MS = 14 * 60 * 1000;
// standard runtime of Websocket
private static final int WS_RUNTIME_MS = 60 * 1000;
// addon time of 1 minute for a new send command
private static final int ADDON_MESSAGE_TIME_MS = 60 * 1000;
// check Socket time elapsed each second
private static final int CHECK_INTERVAL_MS = 1000;
// additional 5 minutes after keep alive
private static final int KEEP_ALIVE_ADDON = 5 * 60 * 1000;
private final Logger logger = LoggerFactory.getLogger(MBWebsocket.class);
private AccountHandler accountHandler;
private boolean running = false;
private Instant runTill = Instant.now();
private @Nullable Session session;
private List<ClientMessage> commandQueue = new ArrayList<>();
private boolean keepAlive = false;
public MBWebsocket(AccountHandler ah) {
accountHandler = ah;
}
/**
* Is called by
* - scheduler every 15 minutes
* - handler sending a command
* - handler requesting refresh
*/
public void run() {
synchronized (this) {
if (running) {
return;
} else {
running = true;
runTill = Instant.now().plusMillis(WS_RUNTIME_MS);
}
}
try {
WebSocketClient client = new WebSocketClient();
client.setMaxIdleTimeout(CONNECT_TIMEOUT_MS);
client.setStopTimeout(CONNECT_TIMEOUT_MS);
ClientUpgradeRequest request = accountHandler.getClientUpgradeRequest();
String websocketURL = accountHandler.getWSUri();
logger.trace("Websocket start {}", websocketURL);
if (Constants.JUNIT_TOKEN.equals(request.getHeader("Authorization"))) {
// avoid unit test requesting real websocket - simply return
return;
}
client.start();
client.connect(this, new URI(websocketURL), request);
while (keepAlive || Instant.now().isBefore(runTill)) {
try {
Thread.sleep(CHECK_INTERVAL_MS);
} catch (InterruptedException ie) {
logger.trace("Websocket interrupted during sleeping - stop executing");
runTill = Instant.MIN;
}
// sends one message per second
if (sendMessage()) {
// add additional runtime to execute and finish command
runTill = runTill.plusMillis(ADDON_MESSAGE_TIME_MS);
}
}
logger.trace("Websocket stop");
client.stop();
client.destroy();
} catch (Throwable t) {
// catch Exceptions of start stop and declare communication error
accountHandler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/mercedesme.account.status.websocket-failure");
logger.warn("Websocket handling exception: {}", t.getMessage());
}
synchronized (this) {
running = false;
}
}
public void setCommand(ClientMessage cm) {
commandQueue.add(cm);
}
private boolean sendMessage() {
if (!commandQueue.isEmpty()) {
ClientMessage message = commandQueue.remove(0);
logger.trace("Send Message {}", message.getAllFields());
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
message.writeTo(baos);
if (session != null) {
session.getRemote().sendBytes(ByteBuffer.wrap(baos.toByteArray()));
}
return true;
} catch (IOException e) {
logger.warn("Error sending message {} : {}", message.getAllFields(), e.getMessage());
}
logger.info("Send Message {} done", message.getAllFields());
}
return false;
}
private void sendAcknowledgeMessage(ClientMessage message) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
message.writeTo(baos);
if (session != null) {
session.getRemote().sendBytes(ByteBuffer.wrap(baos.toByteArray()));
}
} catch (IOException e) {
logger.warn("Error sending acknowledge {} : {}", message.getAllFields(), e.getMessage());
}
}
public boolean isRunning() {
return running;
}
public void interrupt() {
synchronized (this) {
runTill = Instant.MIN;
keepAlive = false;
}
}
public void keepAlive(boolean b) {
if (!keepAlive) {
if (b) {
logger.trace("WebSocket - keep alive start");
}
} else {
if (!b) {
// after keep alive is finished add 5 minutes to cover e.g. door events after trip is finished
runTill = Instant.now().plusMillis(KEEP_ALIVE_ADDON);
logger.trace("Websocket - keep alive stop - run till {}", runTill.toString());
}
}
keepAlive = b;
}
/**
* endpoints
*/
@OnWebSocketMessage
public void onBytes(InputStream is) {
try {
PushMessage pm = VehicleEvents.PushMessage.parseFrom(is);
if (pm.hasVepUpdates()) {
boolean distributed = accountHandler.distributeVepUpdates(pm.getVepUpdates().getUpdatesMap());
if (distributed) {
AcknowledgeVEPUpdatesByVIN ack = AcknowledgeVEPUpdatesByVIN.newBuilder()
.setSequenceNumber(pm.getVepUpdates().getSequenceNumber()).build();
ClientMessage cm = ClientMessage.newBuilder().setAcknowledgeVepUpdatesByVin(ack).build();
sendAcknowledgeMessage(cm);
}
} else if (pm.hasAssignedVehicles()) {
for (int i = 0; i < pm.getAssignedVehicles().getVinsCount(); i++) {
String vin = pm.getAssignedVehicles().getVins(0);
accountHandler.discovery(vin);
}
AcknowledgeAssignedVehicles ack = AcknowledgeAssignedVehicles.newBuilder().build();
ClientMessage cm = ClientMessage.newBuilder().setAcknowledgeAssignedVehicles(ack).build();
sendAcknowledgeMessage(cm);
} else if (pm.hasApptwinCommandStatusUpdatesByVin()) {
AppTwinCommandStatusUpdatesByVIN csubv = pm.getApptwinCommandStatusUpdatesByVin();
accountHandler.commandStatusUpdate(csubv.getUpdatesByVinMap());
AcknowledgeAppTwinCommandStatusUpdatesByVIN ack = AcknowledgeAppTwinCommandStatusUpdatesByVIN
.newBuilder().setSequenceNumber(csubv.getSequenceNumber()).build();
ClientMessage cm = ClientMessage.newBuilder().setAcknowledgeApptwinCommandStatusUpdateByVin(ack)
.build();
sendAcknowledgeMessage(cm);
} else if (pm.hasApptwinPendingCommandRequest()) {
logger.trace("Pending Command {}", pm.getApptwinPendingCommandRequest().getAllFields());
} else if (pm.hasDebugMessage()) {
logger.trace("MB Debug Message: {}", pm.getDebugMessage().getMessage());
} else {
logger.trace("MB Message: {} not handled", pm.getAllFields());
}
} catch (IOException e) {
// don't report thing status errors here.
// Sometimes messages cannot be decoded which doesn't effect the overall functionality
logger.trace("IOException {}", e.getMessage());
} catch (Error err) {
logger.trace("Error caught {}", err.getMessage());
}
}
@OnWebSocketClose
public void onDisconnect(Session session, int statusCode, String reason) {
logger.debug("Disconnected from server. Status {} Reason {}", statusCode, reason);
this.session = null;
// ensure execution stop if disconnect was triggered from server side
interrupt();
}
@OnWebSocketConnect
public void onConnect(Session session) {
accountHandler.updateStatus(ThingStatus.ONLINE);
this.session = session;
}
@OnWebSocketError
public void onError(Throwable t) {
logger.warn("onError {}", t.getMessage());
accountHandler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/mercedesme.account.status.websocket-failure");
}
}

View File

@ -1,92 +0,0 @@
/**
* 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.mercedesme.internal.server;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.mercedesme.internal.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link Utils} class defines an HTTP Server for authentication callbacks
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class Utils {
private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class);
private static final List<Integer> PORTS = new ArrayList<>();
private static int port = 8090;
/**
* Get free port without other Thread interference
*
* @return
*/
public static synchronized int getFreePort() {
while (PORTS.contains(port)) {
port++;
}
PORTS.add(port);
return port;
}
public static synchronized void addPort(int portNr) {
if (PORTS.contains(portNr)) {
LOGGER.warn("Port {} already occupied", portNr);
}
PORTS.add(portNr);
}
public static synchronized void removePort(int portNr) {
PORTS.remove(Integer.valueOf(portNr));
}
public static String getCallbackIP() throws SocketException {
// https://stackoverflow.com/questions/901755/how-to-get-the-ip-of-the-computer-on-linux-through-java
// https://stackoverflow.com/questions/1062041/ip-address-not-obtained-in-java
for (Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces
.hasMoreElements();) {
NetworkInterface iface = ifaces.nextElement();
try {
if (!iface.isLoopback()) {
if (iface.isUp()) {
for (Enumeration<InetAddress> addresses = iface.getInetAddresses(); addresses
.hasMoreElements();) {
InetAddress address = addresses.nextElement();
if (address instanceof Inet4Address) {
return address.getHostAddress();
}
}
}
}
} catch (SocketException se) {
// Calling one network interface failed - continue searching
LOGGER.trace("Network {} failed {}", iface.getName(), se.getMessage());
}
}
throw new SocketException("IP address not detected");
}
public static String getCallbackAddress(String callbackIP, int callbackPort) {
return "http://" + callbackIP + Constants.COLON + callbackPort + Constants.CALLBACK_ENDPOINT;
}
}

View File

@ -12,7 +12,10 @@
*/
package org.openhab.binding.mercedesme.internal.utils;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.types.State;
/**
@ -25,13 +28,21 @@ public class ChannelStateMap {
private String channel;
private String group;
private State state;
private long timestamp;
private Optional<UOMObserver> uomObserver = Optional.empty();
public ChannelStateMap(String ch, String grp, State st, long ts) {
public ChannelStateMap(String ch, String grp, State st) {
channel = ch;
group = grp;
state = st;
timestamp = ts;
}
public ChannelStateMap(String ch, String grp, State st, @Nullable UOMObserver uom) {
channel = ch;
group = grp;
state = st;
if (uom != null) {
uomObserver = Optional.of(uom);
}
}
public String getChannel() {
@ -46,13 +57,17 @@ public class ChannelStateMap {
return state;
}
public long getTimestamp() {
return timestamp;
public boolean hasUomObserver() {
return !uomObserver.isEmpty();
}
public UOMObserver getUomObserver() {
return uomObserver.get();
}
@Override
public String toString() {
return group + ":" + channel + " " + state;
return group + "#" + channel + " " + state;
}
public boolean isValid() {

View File

@ -14,24 +14,38 @@ package org.openhab.binding.mercedesme.internal.utils;
import static org.openhab.binding.mercedesme.internal.Constants.*;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.measure.Unit;
import javax.measure.quantity.Length;
import javax.measure.quantity.Pressure;
import javax.measure.quantity.Speed;
import javax.measure.quantity.Temperature;
import javax.measure.quantity.Volume;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.json.JSONObject;
import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daimler.mbcarkit.proto.VehicleEvents.VehicleAttributeStatus;
/**
* The {@link Mapper} maps a given Json Object towards a channel, group and state
* {@link Mapper} converts Mercedes keys to channel name and group and converts delivered vehicle data
*
* @author Bernd Weymann - Initial contribution
*/
@ -39,168 +53,291 @@ import org.slf4j.LoggerFactory;
public class Mapper {
private static final Logger LOGGER = LoggerFactory.getLogger(Mapper.class);
public static final ChannelStateMap INVALID_MAP = new ChannelStateMap(EMPTY, EMPTY, UnDefType.UNDEF, -1);
public static final ChannelStateMap INVALID_MAP = new ChannelStateMap(EMPTY, EMPTY, UnDefType.UNDEF);
public static final Map<String, String[]> CHANNELS = new HashMap<>();
public static final String TIMESTAMP = "timestamp";
public static final String VALUE = "value";
public static ChannelStateMap getChannelStateMap(JSONObject jo) {
public static Unit<Length> defaultLengthUnit = KILOMETRE_UNIT;
public static Unit<Temperature> defaultTemperatureUnit = SIUnits.CELSIUS;
public static Unit<Pressure> defaultPressureUnit = Units.BAR;
public static Unit<Volume> defaultVolumeUnit = Units.LITRE;
public static Unit<Speed> defaultSpeedUnit = SIUnits.KILOMETRE_PER_HOUR;
public static void initialize(UnitProvider up) {
// Configure Mapper default values
Unit<Length> lengthUnit = up.getUnit(Length.class);
if (ImperialUnits.FOOT.equals(lengthUnit)) {
defaultLengthUnit = ImperialUnits.MILE;
defaultSpeedUnit = ImperialUnits.MILES_PER_HOUR;
defaultPressureUnit = ImperialUnits.POUND_FORCE_SQUARE_INCH;
defaultVolumeUnit = ImperialUnits.GALLON_LIQUID_US;
}
Unit<Temperature> temperatureUnit = up.getUnit(Temperature.class);
defaultTemperatureUnit = temperatureUnit;
}
public static ChannelStateMap getChannelStateMap(String key, VehicleAttributeStatus value) {
if (CHANNELS.isEmpty()) {
init();
}
Set<String> s = jo.keySet();
if (s.size() == 1) {
String id = s.toArray()[0].toString();
String[] ch = CHANNELS.get(id);
if (ch != null) {
State state;
switch (id) {
// Kilometer values
case "odo":
case "rangeelectric":
case "rangeliquid":
state = getKilometers((JSONObject) jo.get(id));
return new ChannelStateMap(ch[0], ch[1], state, getTimestamp((JSONObject) jo.get(id)));
String[] ch = CHANNELS.get(key);
if (ch != null) {
State state;
UOMObserver observer = null;
switch (key) {
// Kilometer values
case MB_KEY_ODO:
case MB_KEY_RANGEELECTRIC:
case MB_KEY_OVERALL_RANGE:
case MB_KEY_RANGELIQUID:
case MB_KEY_DISTANCE_START:
case MB_KEY_DISTANCE_RESET:
Unit<?> lengthUnit = defaultLengthUnit;
if (value.hasDistanceUnit()) {
observer = new UOMObserver(value.getDistanceUnit().toString());
Unit<?> queryUnit = observer.getUnit();
if (queryUnit != null) {
lengthUnit = queryUnit;
} else {
LOGGER.trace("No Unit found for {} - take default ", key);
}
}
state = QuantityType.valueOf(Utils.getDouble(value), lengthUnit);
return new ChannelStateMap(ch[0], ch[1], state, observer);
// Percentages
case "soc":
case "tanklevelpercent":
state = getPercentage((JSONObject) jo.get(id));
return new ChannelStateMap(ch[0], ch[1], state, getTimestamp((JSONObject) jo.get(id)));
// special String Value
case MB_KEY_DRIVEN_TIME_START:
case MB_KEY_DRIVEN_TIME_RESET:
int duration = Utils.getInt(value);
if (duration < 0) {
state = UnDefType.UNDEF;
} else {
state = StringType.valueOf(Utils.getDurationString(duration));
}
return new ChannelStateMap(ch[0], ch[1], state);
// Contacts
case "decklidstatus":
case "doorstatusfrontleft":
case "doorstatusfrontright":
case "doorstatusrearleft":
case "doorstatusrearright":
state = getContact((JSONObject) jo.get(id));
return new ChannelStateMap(ch[0], ch[1], state, getTimestamp((JSONObject) jo.get(id)));
// KiloWatt values
case MB_KEY_CHARGING_POWER:
double power = Utils.getDouble(value);
state = QuantityType.valueOf(Math.max(0, power), KILOWATT_UNIT);
return new ChannelStateMap(ch[0], ch[1], state);
// Number Status
case "lightswitchposition":
case "rooftopstatus":
case "sunroofstatus":
case "windowstatusfrontleft":
case "windowstatusfrontright":
case "windowstatusrearleft":
case "windowstatusrearright":
case "doorlockstatusvehicle":
state = getDecimal((JSONObject) jo.get(id));
return new ChannelStateMap(ch[0], ch[1], state, getTimestamp((JSONObject) jo.get(id)));
case MB_KEY_AVERAGE_SPEED_START:
case MB_KEY_AVERAGE_SPEED_RESET:
Unit<?> speedUnit = defaultSpeedUnit;
if (value.hasSpeedUnit()) {
observer = new UOMObserver(value.getSpeedUnit().toString());
Unit<?> queryUnit = observer.getUnit();
if (queryUnit != null) {
lengthUnit = observer.getUnit();
} else {
LOGGER.trace("No Unit found for {} - take default ", key);
}
}
double speed = Utils.getDouble(value);
state = QuantityType.valueOf(Math.max(0, speed), speedUnit);
return new ChannelStateMap(ch[0], ch[1], state, observer);
// Switches
case "interiorLightsFront":
case "interiorLightsRear":
case "readingLampFrontLeft":
case "readingLampFrontRight":
state = getOnOffType((JSONObject) jo.get(id));
return new ChannelStateMap(ch[0], ch[1], state, getTimestamp((JSONObject) jo.get(id)));
// KiloWatt/Hour values
case MB_KEY_ELECTRICCONSUMPTIONSTART:
case MB_KEY_ELECTRICCONSUMPTIONRESET:
double consumptionEv = Utils.getDouble(value);
state = new DecimalType(consumptionEv);
if (value.hasElectricityConsumptionUnit()) {
observer = new UOMObserver(value.getElectricityConsumptionUnit().toString());
} else {
LOGGER.trace("Don't have electric consumption unit for {}", key);
}
return new ChannelStateMap(ch[0], ch[1], state, observer);
case "doorlockstatusdecklid":
case "doorlockstatusgas":
state = getOnOffTypeLock((JSONObject) jo.get(id));
return new ChannelStateMap(ch[0], ch[1], state, getTimestamp((JSONObject) jo.get(id)));
// Litre values
case MB_KEY_LIQUIDCONSUMPTIONSTART:
case MB_KEY_LIQUIDCONSUMPTIONRESET:
double consumptionComb = Utils.getDouble(value);
state = new DecimalType(consumptionComb);
if (value.hasCombustionConsumptionUnit()) {
observer = new UOMObserver(value.getCombustionConsumptionUnit().toString());
}
return new ChannelStateMap(ch[0], ch[1], state, observer);
// Angle
case "positionHeading":
state = getAngle((JSONObject) jo.get(id));
return new ChannelStateMap(ch[0], ch[1], state, getTimestamp((JSONObject) jo.get(id)));
default:
LOGGER.trace("No mapping available for {}", id);
}
} else {
LOGGER.trace("No mapping available for {}", id);
// Time - end of charging
case MB_KEY_ENDOFCHARGETIME:
if (Utils.isNil(value)) {
state = UnDefType.UNDEF;
} else {
// int value is representing "minutes after Midnight!
Instant time = Instant.ofEpochMilli(value.getTimestampInMs());
long minutesAddon = Utils.getInt(value);
time.plus(minutesAddon, ChronoUnit.MINUTES);
state = Utils.getEndOfChargeTime(time.toEpochMilli(), minutesAddon);
if (Locale.US.getCountry().equals(Utils.getCountry())) {
observer = new UOMObserver(UOMObserver.TIME_US);
} else {
observer = new UOMObserver(UOMObserver.TIME_ROW);
}
}
return new ChannelStateMap(ch[0], ch[1], state, observer);
// DateTime - last Update
case MB_KEY_TIRE_PRESS_MEAS_TIMESTAMP:
if (Utils.isNil(value)) {
state = UnDefType.UNDEF;
} else {
state = Utils.getDateTimeType(value.getTimestampInMs());
}
if (Locale.US.getCountry().equals(Utils.getCountry())) {
observer = new UOMObserver(UOMObserver.TIME_US);
} else {
observer = new UOMObserver(UOMObserver.TIME_ROW);
}
return new ChannelStateMap(ch[0], ch[1], state, observer);
// Percentages
case MB_KEY_SOC:
case MB_KEY_TANKLEVELPERCENT:
double level = Utils.getDouble(value);
state = QuantityType.valueOf(level, Units.PERCENT);
return new ChannelStateMap(ch[0], ch[1], state);
// Contacts
case MB_KEY_DOORSTATUSFRONTRIGHT:
case MB_KEY_DOORSTATUSFRONTLEFT:
case MB_KEY_DOORSTATUSREARRIGHT:
case MB_KEY_DOORSTATUSREARLEFT:
case MB_KEY_DECKLIDSTATUS:
case MB_KEY_ENGINE_HOOD_STATUS:
if (Utils.isNil(value)) {
state = UnDefType.UNDEF;
} else {
state = getContact(value.getBoolValue());
}
return new ChannelStateMap(ch[0], ch[1], state);
// Number Status
case MB_KEY_DOOR_LOCK_STATUS_OVERALL:
case MB_KEY_WINDOW_STATUS_OVERALL:
case MB_KEY_DOOR_STATUS_OVERALL:
case MB_KEY_IGNITIONSTATE:
case MB_KEY_SUNROOFSTATUS:
case MB_KEY_SUNROOF_STATUS_FRONT_BLIND:
case MB_KEY_SUNROOF_STATUS_REAR_BLIND:
case MB_KEY_ROOFTOPSTATUS:
case MB_KEY_WINDOWSTATUSFRONTLEFT:
case MB_KEY_WINDOWSTATUSFRONTRIGHT:
case MB_KEY_WINDOWSTATUSREARLEFT:
case MB_KEY_WINDOWSTATUSREARRIGHT:
case MB_KEY_WINDOW_STATUS_REAR_RIGHT_BLIND:
case MB_KEY_WINDOW_STATUS_REAR_LEFT_BLIND:
case MB_KEY_WINDOW_STATUS_REAR_BLIND:
case MB_KEY_FLIP_WINDOW_STATUS:
case MB_KEY_STARTER_BATTERY_STATE:
case MB_KEY_TIREWARNINGSRDK:
case MB_KEY_SERVICEINTERVALDAYS:
case MB_KEY_CHARGE_FLAP_DC_STATUS:
case MB_KEY_CHARGE_COUPLER_AC_STATUS:
case MB_KEY_CHARGE_COUPLER_DC_STATUS:
case MB_KEY_CHARGE_COUPLER_DC_LOCK_STATUS:
case MB_KEY_TIRE_SENSOR_AVAILABLE:
int stateNumberInteger = Utils.getInt(value);
if (stateNumberInteger < 0) {
state = UnDefType.UNDEF;
} else {
state = new DecimalType(stateNumberInteger);
}
return new ChannelStateMap(ch[0], ch[1], state);
case MB_KEY_TIRE_MARKER_FRONT_RIGHT:
case MB_KEY_TIRE_MARKER_FRONT_LEFT:
case MB_KEY_TIRE_MARKER_REAR_RIGHT:
case MB_KEY_TIRE_MARKER_REAR_LEFT:
double stateNumberDouble = Utils.getDouble(value);
if (stateNumberDouble < 0) {
state = UnDefType.UNDEF;
} else {
state = new DecimalType(stateNumberDouble);
}
return new ChannelStateMap(ch[0], ch[1], state);
// Switches
case MB_KEY_PARKBRAKESTATUS:
case MB_KEY_PRECOND_NOW:
case MB_KEY_PRECOND_SEAT_FRONT_RIGHT:
case MB_KEY_PRECOND_SEAT_FRONT_LEFT:
case MB_KEY_PRECOND_SEAT_REAR_RIGHT:
case MB_KEY_PRECOND_SEAT_REAR_LEFT:
case MB_KEY_WARNINGBRAKEFLUID:
case MB_KEY_WARNINGBRAKELININGWEAR:
case MB_KEY_WARNINGWASHWATER:
case MB_KEY_WARNINGCOOLANTLEVELLOW:
case MB_KEY_WARNINGENGINELIGHT:
case MB_KEY_CHARGINGACTIVE:
if (Utils.isNil(value)) {
state = UnDefType.UNDEF;
} else {
if (value.hasBoolValue()) {
state = OnOffType.from(value.getBoolValue());
} else {
state = UnDefType.UNDEF;
}
}
return new ChannelStateMap(ch[0], ch[1], state);
// Switches - lock values with reversed boolean interpretation
case MB_KEY_DOORLOCKSTATUSFRONTRIGHT:
case MB_KEY_DOORLOCKSTATUSFRONTLEFT:
case MB_KEY_DOORLOCKSTATUSREARRIGHT:
case MB_KEY_DOORLOCKSTATUSREARLEFT:
case MB_KEY_DOORLOCKSTATUSDECKLID:
case MB_KEY_DOORLOCKSTATUSGAS:
if (Utils.isNil(value)) {
state = UnDefType.UNDEF;
} else {
// sad but true - false means locked
state = OnOffType.from(!value.getBoolValue());
}
return new ChannelStateMap(ch[0], ch[1], state);
// Angle
case MB_KEY_POSITION_HEADING:
double heading = Utils.getDouble(value);
if (heading < 0) {
state = UnDefType.UNDEF;
} else {
state = QuantityType.valueOf(heading, Units.DEGREE_ANGLE);
}
return new ChannelStateMap(ch[0], ch[1], state);
// tires
case MB_KEY_TIREPRESSURE_FRONT_LEFT:
case MB_KEY_TIREPRESSURE_FRONT_RIGHT:
case MB_KEY_TIREPRESSURE_REAR_LEFT:
case MB_KEY_TIREPRESSURE_REAR_RIGHT:
Unit<?> pressureUnit = defaultPressureUnit;
if (value.hasPressureUnit()) {
observer = new UOMObserver(value.getPressureUnit().toString());
Unit<?> queryUnit = observer.getUnit();
if (queryUnit != null) {
pressureUnit = queryUnit;
} else {
LOGGER.trace("No Unit found for {} - take default ", key);
}
}
double pressure = Utils.getDouble(value);
state = QuantityType.valueOf(pressure, pressureUnit);
return new ChannelStateMap(ch[0], ch[1], state, observer);
default:
break;
}
} else {
LOGGER.debug("More than one key found {}", s);
}
return INVALID_MAP;
}
private static long getTimestamp(JSONObject jo) {
if (jo.has(TIMESTAMP)) {
return jo.getLong(TIMESTAMP);
}
return -1;
}
private static State getOnOffType(JSONObject jo) {
if (jo.has(VALUE)) {
String value = jo.get(VALUE).toString();
boolean b = Boolean.valueOf(value);
return OnOffType.from(b);
private static State getContact(boolean b) {
if (!b) {
return OpenClosedType.CLOSED;
} else {
LOGGER.warn("JSONObject contains no value {}", jo);
return UnDefType.UNDEF;
}
}
private static State getOnOffTypeLock(JSONObject jo) {
if (jo.has(VALUE)) {
String value = jo.get(VALUE).toString();
boolean b = Boolean.valueOf(value);
// Yes, false is locked and true unlocked
// https://developer.mercedes-benz.com/products/vehicle_lock_status/specifications/vehicle_lock_status_api
return OnOffType.from(!b);
} else {
LOGGER.warn("JSONObject contains no value {}", jo);
return UnDefType.UNDEF;
}
}
private static State getAngle(JSONObject jo) {
if (jo.has(VALUE)) {
String value = jo.get(VALUE).toString();
return QuantityType.valueOf(Double.valueOf(value), Units.DEGREE_ANGLE);
} else {
LOGGER.warn("JSONObject contains no value {}", jo);
return UnDefType.UNDEF;
}
}
private static State getDecimal(JSONObject jo) {
if (jo.has(VALUE)) {
String value = jo.get(VALUE).toString();
return DecimalType.valueOf(value);
} else {
LOGGER.warn("JSONObject contains no value {}", jo);
return UnDefType.UNDEF;
}
}
private static State getContact(JSONObject jo) {
if (jo.has(VALUE)) {
String value = jo.get(VALUE).toString();
boolean b = Boolean.valueOf(value);
if (!b) {
return OpenClosedType.CLOSED;
} else {
return OpenClosedType.OPEN;
}
} else {
LOGGER.warn("JSONObject contains no value {}", jo);
return UnDefType.UNDEF;
}
}
private static State getKilometers(JSONObject jo) {
if (jo.has(VALUE)) {
String value = jo.get(VALUE).toString();
return QuantityType.valueOf(Integer.valueOf(value), KILOMETRE_UNIT);
} else {
LOGGER.warn("JSONObject contains no value {}", jo);
return UnDefType.UNDEF;
}
}
private static State getPercentage(JSONObject jo) {
if (jo.has(VALUE)) {
String value = jo.get(VALUE).toString();
return QuantityType.valueOf(Integer.valueOf(value), Units.PERCENT);
} else {
LOGGER.warn("JSONObject contains no value {}", jo);
return UnDefType.UNDEF;
return OpenClosedType.OPEN;
}
}
@ -208,30 +345,93 @@ public class Mapper {
* Mapping of json id towards channel group and id
*/
private static void init() {
CHANNELS.put("odo", new String[] { "mileage", GROUP_RANGE });
CHANNELS.put("rangeelectric", new String[] { "range-electric", GROUP_RANGE });
CHANNELS.put("soc", new String[] { "soc", GROUP_RANGE });
CHANNELS.put("rangeliquid", new String[] { "range-fuel", GROUP_RANGE });
CHANNELS.put("tanklevelpercent", new String[] { "fuel-level", GROUP_RANGE });
CHANNELS.put("decklidstatus", new String[] { "deck-lid", GROUP_DOORS });
CHANNELS.put("doorstatusfrontleft", new String[] { "driver-front", GROUP_DOORS });
CHANNELS.put("doorstatusfrontright", new String[] { "passenger-front", GROUP_DOORS });
CHANNELS.put("doorstatusrearleft", new String[] { "driver-rear", GROUP_DOORS });
CHANNELS.put("doorstatusrearright", new String[] { "passenger-rear", GROUP_DOORS });
CHANNELS.put("interiorLightsFront", new String[] { "interior-front", GROUP_LIGHTS });
CHANNELS.put("interiorLightsRear", new String[] { "interior-rear", GROUP_LIGHTS });
CHANNELS.put("lightswitchposition", new String[] { "light-switch", GROUP_LIGHTS });
CHANNELS.put("readingLampFrontLeft", new String[] { "reading-left", GROUP_LIGHTS });
CHANNELS.put("readingLampFrontRight", new String[] { "reading-right", GROUP_LIGHTS });
CHANNELS.put("rooftopstatus", new String[] { "rooftop", GROUP_DOORS });
CHANNELS.put("sunroofstatus", new String[] { "sunroof", GROUP_DOORS });
CHANNELS.put("windowstatusfrontleft", new String[] { "driver-front", GROUP_WINDOWS });
CHANNELS.put("windowstatusfrontright", new String[] { "passenger-front", GROUP_WINDOWS });
CHANNELS.put("windowstatusrearleft", new String[] { "driver-rear", GROUP_WINDOWS });
CHANNELS.put("windowstatusrearright", new String[] { "passenger-rear", GROUP_WINDOWS });
CHANNELS.put("doorlockstatusvehicle", new String[] { "doors", GROUP_LOCK });
CHANNELS.put("doorlockstatusdecklid", new String[] { "deck-lid", GROUP_LOCK });
CHANNELS.put("doorlockstatusgas", new String[] { "flap", GROUP_LOCK });
CHANNELS.put("positionHeading", new String[] { "heading", GROUP_LOCATION });
CHANNELS.put(MB_KEY_DOOR_LOCK_STATUS_OVERALL, new String[] { OH_CHANNEL_LOCK, GROUP_VEHICLE });
CHANNELS.put(MB_KEY_WINDOW_STATUS_OVERALL, new String[] { OH_CHANNEL_WINDOWS, GROUP_VEHICLE });
CHANNELS.put(MB_KEY_DOOR_STATUS_OVERALL, new String[] { OH_CHANNEL_DOOR_STATUS, GROUP_VEHICLE });
CHANNELS.put(MB_KEY_IGNITIONSTATE, new String[] { OH_CHANNEL_IGNITION, GROUP_VEHICLE });
CHANNELS.put(MB_KEY_PARKBRAKESTATUS, new String[] { OH_CHANNEL_PARK_BRAKE, GROUP_VEHICLE });
CHANNELS.put(MB_KEY_DOORSTATUSFRONTRIGHT, new String[] { OH_CHANNEL_FRONT_RIGHT, GROUP_DOORS });
CHANNELS.put(MB_KEY_DOORSTATUSFRONTLEFT, new String[] { OH_CHANNEL_FRONT_LEFT, GROUP_DOORS });
CHANNELS.put(MB_KEY_DOORSTATUSREARRIGHT, new String[] { OH_CHANNEL_REAR_RIGHT, GROUP_DOORS });
CHANNELS.put(MB_KEY_DOORSTATUSREARLEFT, new String[] { OH_CHANNEL_REAR_LEFT, GROUP_DOORS });
CHANNELS.put(MB_KEY_DECKLIDSTATUS, new String[] { OH_CHANNEL_DECK_LID, GROUP_DOORS });
CHANNELS.put(MB_KEY_ENGINE_HOOD_STATUS, new String[] { OH_CHANNEL_ENGINE_HOOD, GROUP_DOORS });
CHANNELS.put(MB_KEY_SUNROOFSTATUS, new String[] { OH_CHANNEL_SUNROOF, GROUP_DOORS });
CHANNELS.put(MB_KEY_SUNROOF_STATUS_FRONT_BLIND, new String[] { OH_CHANNEL_SUNROOF_FRONT_BLIND, GROUP_DOORS });
CHANNELS.put(MB_KEY_SUNROOF_STATUS_REAR_BLIND, new String[] { OH_CHANNEL_SUNROOF_REAR_BLIND, GROUP_DOORS });
CHANNELS.put(MB_KEY_ROOFTOPSTATUS, new String[] { OH_CHANNEL_ROOFTOP, GROUP_DOORS });
CHANNELS.put(MB_KEY_DOORLOCKSTATUSFRONTRIGHT, new String[] { OH_CHANNEL_FRONT_RIGHT, GROUP_LOCK });
CHANNELS.put(MB_KEY_DOORLOCKSTATUSFRONTLEFT, new String[] { OH_CHANNEL_FRONT_LEFT, GROUP_LOCK });
CHANNELS.put(MB_KEY_DOORLOCKSTATUSREARRIGHT, new String[] { OH_CHANNEL_REAR_RIGHT, GROUP_LOCK });
CHANNELS.put(MB_KEY_DOORLOCKSTATUSREARLEFT, new String[] { OH_CHANNEL_REAR_LEFT, GROUP_LOCK });
CHANNELS.put(MB_KEY_DOORLOCKSTATUSDECKLID, new String[] { OH_CHANNEL_DECK_LID, GROUP_LOCK });
CHANNELS.put(MB_KEY_DOORLOCKSTATUSGAS, new String[] { OH_CHANNEL_GAS_FLAP, GROUP_LOCK });
CHANNELS.put(MB_KEY_WINDOWSTATUSFRONTLEFT, new String[] { OH_CHANNEL_FRONT_LEFT, GROUP_WINDOWS });
CHANNELS.put(MB_KEY_WINDOWSTATUSFRONTRIGHT, new String[] { OH_CHANNEL_FRONT_RIGHT, GROUP_WINDOWS });
CHANNELS.put(MB_KEY_WINDOWSTATUSREARLEFT, new String[] { OH_CHANNEL_REAR_LEFT, GROUP_WINDOWS });
CHANNELS.put(MB_KEY_WINDOWSTATUSREARRIGHT, new String[] { OH_CHANNEL_REAR_RIGHT, GROUP_WINDOWS });
CHANNELS.put(MB_KEY_WINDOW_STATUS_REAR_RIGHT_BLIND,
new String[] { OH_CHANNEL_REAR_RIGHT_BLIND, GROUP_WINDOWS });
CHANNELS.put(MB_KEY_WINDOW_STATUS_REAR_LEFT_BLIND, new String[] { OH_CHANNEL_REAR_LEFT_BLIND, GROUP_WINDOWS });
CHANNELS.put(MB_KEY_WINDOW_STATUS_REAR_BLIND, new String[] { OH_CHANNEL_REAR_BLIND, GROUP_WINDOWS });
CHANNELS.put(MB_KEY_FLIP_WINDOW_STATUS, new String[] { OH_CHANNEL_FLIP_WINDOW, GROUP_WINDOWS });
CHANNELS.put(MB_KEY_PRECOND_NOW, new String[] { OH_CHANNEL_ACTIVE, GROUP_HVAC });
CHANNELS.put(MB_KEY_PRECOND_SEAT_FRONT_RIGHT, new String[] { OH_CHANNEL_FRONT_RIGHT, GROUP_HVAC });
CHANNELS.put(MB_KEY_PRECOND_SEAT_FRONT_LEFT, new String[] { OH_CHANNEL_FRONT_LEFT, GROUP_HVAC });
CHANNELS.put(MB_KEY_PRECOND_SEAT_REAR_RIGHT, new String[] { OH_CHANNEL_REAR_RIGHT, GROUP_HVAC });
CHANNELS.put(MB_KEY_PRECOND_SEAT_REAR_LEFT, new String[] { OH_CHANNEL_REAR_LEFT, GROUP_HVAC });
// temperaturePoints - special handling: sets zone & temperature
CHANNELS.put(MB_KEY_STARTER_BATTERY_STATE, new String[] { OH_CHANNEL_STARTER_BATTERY, GROUP_SERVICE });
CHANNELS.put(MB_KEY_WARNINGBRAKEFLUID, new String[] { OH_CHANNEL_BRAKE_FLUID, GROUP_SERVICE });
CHANNELS.put(MB_KEY_WARNINGWASHWATER, new String[] { OH_CHANNEL_WASH_WATER, GROUP_SERVICE });
CHANNELS.put(MB_KEY_WARNINGBRAKELININGWEAR, new String[] { OH_CHANNEL_BRAKE_LINING_WEAR, GROUP_SERVICE });
CHANNELS.put(MB_KEY_WARNINGCOOLANTLEVELLOW, new String[] { OH_CHANNEL_COOLANT_FLUID, GROUP_SERVICE });
CHANNELS.put(MB_KEY_WARNINGENGINELIGHT, new String[] { OH_CHANNEL_ENGINE, GROUP_SERVICE });
CHANNELS.put(MB_KEY_TIREWARNINGSRDK, new String[] { OH_CHANNEL_TIRES_RDK, GROUP_SERVICE });
CHANNELS.put(MB_KEY_SERVICEINTERVALDAYS, new String[] { OH_CHANNEL_SERVICE_DAYS, GROUP_SERVICE });
CHANNELS.put(MB_KEY_ODO, new String[] { OH_CHANNEL_MILEAGE, GROUP_RANGE });
CHANNELS.put(MB_KEY_RANGEELECTRIC, new String[] { OH_CHANNEL_RANGE_ELECTRIC, GROUP_RANGE });
CHANNELS.put(MB_KEY_SOC, new String[] { MB_KEY_SOC, GROUP_RANGE });
CHANNELS.put(MB_KEY_RANGELIQUID, new String[] { OH_CHANNEL_RANGE_FUEL, GROUP_RANGE });
CHANNELS.put(MB_KEY_OVERALL_RANGE, new String[] { OH_CHANNEL_RANGE_HYBRID, GROUP_RANGE });
CHANNELS.put(MB_KEY_TANKLEVELPERCENT, new String[] { OH_CHANNEL_FUEL_LEVEL, GROUP_RANGE });
CHANNELS.put(MB_KEY_CHARGE_FLAP_DC_STATUS, new String[] { OH_CHANNEL_CHARGE_FLAP, GROUP_CHARGE });
CHANNELS.put(MB_KEY_CHARGE_COUPLER_AC_STATUS, new String[] { OH_CHANNEL_COUPLER_AC, GROUP_CHARGE });
CHANNELS.put(MB_KEY_CHARGE_COUPLER_DC_STATUS, new String[] { OH_CHANNEL_COUPLER_DC, GROUP_CHARGE });
CHANNELS.put(MB_KEY_CHARGE_COUPLER_DC_LOCK_STATUS, new String[] { OH_CHANNEL_COUPLER_LOCK, GROUP_CHARGE });
CHANNELS.put(MB_KEY_CHARGINGACTIVE, new String[] { OH_CHANNEL_ACTIVE, GROUP_CHARGE });
CHANNELS.put(MB_KEY_CHARGING_POWER, new String[] { OH_CHANNEL_POWER, GROUP_CHARGE });
CHANNELS.put(MB_KEY_ENDOFCHARGETIME, new String[] { OH_CHANNEL_END_TIME, GROUP_CHARGE });
CHANNELS.put(MB_KEY_POSITION_HEADING, new String[] { OH_CHANNEL_HEADING, GROUP_POSITION });
CHANNELS.put(MB_KEY_DISTANCE_START, new String[] { OH_CHANNEL_DISTANCE, GROUP_TRIP });
CHANNELS.put(MB_KEY_DRIVEN_TIME_START, new String[] { OH_CHANNEL_TIME, GROUP_TRIP });
CHANNELS.put(MB_KEY_AVERAGE_SPEED_START, new String[] { OH_CHANNEL_AVG_SPEED, GROUP_TRIP });
CHANNELS.put(MB_KEY_ELECTRICCONSUMPTIONSTART, new String[] { OH_CHANNEL_CONS_EV, GROUP_TRIP });
CHANNELS.put(MB_KEY_LIQUIDCONSUMPTIONSTART, new String[] { OH_CHANNEL_CONS_CONV, GROUP_TRIP });
CHANNELS.put(MB_KEY_DISTANCE_RESET, new String[] { OH_CHANNEL_DISTANCE_RESET, GROUP_TRIP });
CHANNELS.put(MB_KEY_DRIVEN_TIME_RESET, new String[] { OH_CHANNEL_TIME_RESET, GROUP_TRIP });
CHANNELS.put(MB_KEY_AVERAGE_SPEED_RESET, new String[] { OH_CHANNEL_AVG_SPEED_RESET, GROUP_TRIP });
CHANNELS.put(MB_KEY_ELECTRICCONSUMPTIONRESET, new String[] { OH_CHANNEL_CONS_EV_RESET, GROUP_TRIP });
CHANNELS.put(MB_KEY_LIQUIDCONSUMPTIONRESET, new String[] { OH_CHANNEL_CONS_CONV_RESET, GROUP_TRIP });
CHANNELS.put(MB_KEY_TIREPRESSURE_REAR_RIGHT, new String[] { OH_CHANNEL_PRESSURE_REAR_RIGHT, GROUP_TIRES });
CHANNELS.put(MB_KEY_TIREPRESSURE_FRONT_RIGHT, new String[] { OH_CHANNEL_PRESSURE_FRONT_RIGHT, GROUP_TIRES });
CHANNELS.put(MB_KEY_TIREPRESSURE_REAR_LEFT, new String[] { OH_CHANNEL_PRESSURE_REAR_LEFT, GROUP_TIRES });
CHANNELS.put(MB_KEY_TIREPRESSURE_FRONT_LEFT, new String[] { OH_CHANNEL_PRESSURE_FRONT_LEFT, GROUP_TIRES });
CHANNELS.put(MB_KEY_TIRE_MARKER_FRONT_RIGHT, new String[] { OH_CHANNEL_MARKER_REAR_RIGHT, GROUP_TIRES });
CHANNELS.put(MB_KEY_TIRE_MARKER_FRONT_LEFT, new String[] { OH_CHANNEL_MARKER_FRONT_RIGHT, GROUP_TIRES });
CHANNELS.put(MB_KEY_TIRE_MARKER_REAR_RIGHT, new String[] { OH_CHANNEL_MARKER_REAR_LEFT, GROUP_TIRES });
CHANNELS.put(MB_KEY_TIRE_MARKER_REAR_LEFT, new String[] { OH_CHANNEL_MARKER_FRONT_LEFT, GROUP_TIRES });
CHANNELS.put(MB_KEY_TIRE_SENSOR_AVAILABLE, new String[] { OH_CHANNEL_SENSOR_AVAILABLE, GROUP_TIRES });
CHANNELS.put(MB_KEY_TIRE_PRESS_MEAS_TIMESTAMP, new String[] { OH_CHANNEL_LAST_UPDATE, GROUP_TIRES });
}
}

View File

@ -0,0 +1,135 @@
/**
* 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.mercedesme.internal.utils;
import java.util.HashMap;
import java.util.Map;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mercedesme.internal.Constants;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link UOMObserver} responsible to identify Unit and StatePattern for a Mercedes VehicleAttribute
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class UOMObserver {
// Values delivered by Mercedes API
public static final String LENGTH_KM_UNIT = "KILOMETERS";
public static final String LENGTH_MILES_UNIT = "MILES";
public static final String SPEED_KM_UNIT = "KM_PER_HOUR";
public static final String SPEED_MILES_UNIT = "M_PER_HOUR";
public static final String CELSIUS_UNIT = "CELSIUS";
public static final String FAHRENHEIT_UNIT = "FAHRENHEIT";
public static final String BAR_UNIT = "BAR";
public static final String PSI_UNIT = "PSI";
public static final String KPA_UNIT = "KPA";
public static final String CONSUMPTION_ELECTRIC_KM = "KM_PER_KWH";
public static final String CONSUMPTION_ELECTRIC_MI = "M_PER_KWH";
public static final String CONSUMPTION_ELECTRIC_100KM = "KWH_PER_100KM";
public static final String CONSUMPTION_ELECTRIC_100MI = "KWH_PER_100MI";
public static final String CONSUMPTION_LIQUID_100KM = "LITER_PER_100KM";
public static final String CONSUMPTION_LIQUID_KM_PER_LITER = "KM_PER_LITER";
public static final String CONSUMPTION_LIQUID_MPG_UK = "MPG_UK";
public static final String CONSUMPTION_LIQUID_MPG_US = "MPG_US";
public static final String CONSUMPTION_LIQUID_MI = "MPGE";
public static final String CONSUMPTION_GAS_100KM = "KG_PER_100KM";
public static final String TIME_US = "TIME_US";
public static final String TIME_ROW = "TIME_ROW";
private static final Map<String, String> PATTERN_MAP = new HashMap<>();
private static final Map<String, Unit<?>> UNIT_MAP = new HashMap<>();
private String label = Constants.NOT_SET;
private final Logger logger = LoggerFactory.getLogger(UOMObserver.class);
public UOMObserver(String unitLabel) {
if (PATTERN_MAP.isEmpty()) {
PATTERN_MAP.put(LENGTH_KM_UNIT, "%.1f km");
PATTERN_MAP.put(LENGTH_MILES_UNIT, "%.1f mi");
PATTERN_MAP.put(SPEED_KM_UNIT, "%.0f km/h");
PATTERN_MAP.put(SPEED_MILES_UNIT, "%.0f mph");
PATTERN_MAP.put(CELSIUS_UNIT, "%.1f °C");
PATTERN_MAP.put(FAHRENHEIT_UNIT, "%.0f °F");
PATTERN_MAP.put(BAR_UNIT, "%.1f bar");
PATTERN_MAP.put(KPA_UNIT, "%.1f kPa");
PATTERN_MAP.put(PSI_UNIT, "%.1f psi");
PATTERN_MAP.put(CONSUMPTION_ELECTRIC_KM, "km/kWh");
PATTERN_MAP.put(CONSUMPTION_ELECTRIC_MI, "m/kWh");
PATTERN_MAP.put(CONSUMPTION_ELECTRIC_100KM, "kWh/100km");
PATTERN_MAP.put(CONSUMPTION_ELECTRIC_100MI, "kWh/100mi");
PATTERN_MAP.put(CONSUMPTION_LIQUID_100KM, "l/100km");
PATTERN_MAP.put(CONSUMPTION_LIQUID_KM_PER_LITER, "km/l");
PATTERN_MAP.put(CONSUMPTION_LIQUID_MPG_UK, "mi/g");
PATTERN_MAP.put(CONSUMPTION_LIQUID_MPG_US, "mi/g");
PATTERN_MAP.put(CONSUMPTION_LIQUID_MI, "mpge");
PATTERN_MAP.put(TIME_US, "%1$tA, %1$td.%1$tm. %1$tI:%1$tM %1$Tp");
PATTERN_MAP.put(TIME_ROW, "%1$tA, %1$td.%1$tm. %1$tH:%1$tM");
UNIT_MAP.put(LENGTH_KM_UNIT, Constants.KILOMETRE_UNIT);
UNIT_MAP.put(LENGTH_MILES_UNIT, ImperialUnits.MILE);
UNIT_MAP.put(SPEED_KM_UNIT, SIUnits.KILOMETRE_PER_HOUR);
UNIT_MAP.put(SPEED_MILES_UNIT, ImperialUnits.MILES_PER_HOUR);
UNIT_MAP.put(CELSIUS_UNIT, SIUnits.CELSIUS);
UNIT_MAP.put(FAHRENHEIT_UNIT, ImperialUnits.FAHRENHEIT);
UNIT_MAP.put(BAR_UNIT, Units.BAR);
UNIT_MAP.put(KPA_UNIT, Constants.KPA_UNIT);
UNIT_MAP.put(PSI_UNIT, ImperialUnits.POUND_FORCE_SQUARE_INCH);
UNIT_MAP.put(CONSUMPTION_ELECTRIC_KM, Constants.KILOWATT_HOUR_UNIT);
UNIT_MAP.put(CONSUMPTION_ELECTRIC_MI, Constants.KILOWATT_HOUR_UNIT);
UNIT_MAP.put(CONSUMPTION_ELECTRIC_100KM, Constants.KILOWATT_HOUR_UNIT);
UNIT_MAP.put(CONSUMPTION_ELECTRIC_100MI, Constants.KILOWATT_HOUR_UNIT);
UNIT_MAP.put(CONSUMPTION_LIQUID_100KM, Units.LITRE);
UNIT_MAP.put(CONSUMPTION_LIQUID_MI, ImperialUnits.GALLON_LIQUID_US);
}
if (!PATTERN_MAP.containsKey(unitLabel)) {
logger.trace("No mapping found for {}", unitLabel);
}
label = unitLabel;
}
public String getLabel() {
return label;
}
@Nullable
public Unit<?> getUnit() {
return UNIT_MAP.get(label);
}
@Nullable
public String getPattern(String group, String channel) {
String pattern = PATTERN_MAP.get(label);
if (Constants.GROUP_RANGE.equals(group) && pattern != null) {
if ("home-distance".equals(channel)) {
return pattern.replace("1", "3");
}
return pattern.replace("1", "0");
}
return pattern;
}
public boolean equals(UOMObserver compare) {
return label.equals(compare.getLabel());
}
}

View File

@ -0,0 +1,718 @@
/**
* 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.mercedesme.internal.utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONObject;
import org.openhab.binding.mercedesme.internal.Constants;
import org.openhab.binding.mercedesme.internal.MercedesMeHandlerFactory;
import org.openhab.binding.mercedesme.internal.server.AuthService;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.types.CommandOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daimler.mbcarkit.proto.VehicleCommands.ChargeProgramConfigure.ChargeProgram;
import com.daimler.mbcarkit.proto.VehicleCommands.TemperatureConfigure.TemperaturePoint.Zone;
import com.daimler.mbcarkit.proto.VehicleEvents;
import com.daimler.mbcarkit.proto.VehicleEvents.ChargeProgramParameters;
import com.daimler.mbcarkit.proto.VehicleEvents.ChargeProgramsValue;
import com.daimler.mbcarkit.proto.VehicleEvents.TemperaturePointsValue;
import com.daimler.mbcarkit.proto.VehicleEvents.VEPUpdate;
import com.daimler.mbcarkit.proto.VehicleEvents.VVRTimeProfile;
import com.daimler.mbcarkit.proto.VehicleEvents.VehicleAttributeStatus;
import com.daimler.mbcarkit.proto.VehicleEvents.WeeklyProfileValue;
import com.daimler.mbcarkit.proto.VehicleEvents.WeeklySetting;
import com.daimler.mbcarkit.proto.VehicleEvents.WeeklySettingsHeadUnitValue;
import com.google.gson.Gson;
import com.google.protobuf.Descriptors.FieldDescriptor;
/**
* {@link Utils} provides several helper functions used from different classes
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class Utils {
private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class);
private static final List<Integer> PORTS = new ArrayList<>();
private static final List<CommandOption> FAHRENHEIT_COMMAND_OPTIONS = new ArrayList<>();
private static final List<CommandOption> CELSIUS_COMMAND_OPTIONS = new ArrayList<>();
private static final int R = 6371; // Radius of the earth
private static int port = 8090;
private static TimeZoneProvider timeZoneProvider = new TimeZoneProvider() {
@Override
public ZoneId getTimeZone() {
return ZoneId.systemDefault();
}
};
private static LocaleProvider localeProvider = new LocaleProvider() {
@Override
public Locale getLocale() {
return Locale.getDefault();
}
};
public static final Gson GSON = new Gson();
public static final Map<String, Integer> ZONE_HASHMAP = new HashMap<>();
public static final Map<String, Integer> PROGRAM_HASHMAP = new HashMap<>();
public static void initialize(TimeZoneProvider tzp, LocaleProvider lp) {
timeZoneProvider = tzp;
localeProvider = lp;
}
/**
* Getting openHAB DateTimeType from given milliseconds according to configured TimeZone
*
* @param ms - milliseconds in epoch milli
* @return openHAB DateTimeType according to configured TimeZone
*/
public static DateTimeType getDateTimeType(long ms) {
Instant timestamp = Instant.ofEpochMilli(ms);
return new DateTimeType(timestamp.atZone(timeZoneProvider.getTimeZone()));
}
/**
* Calculates the DateTime of charge end according to given Mercedes parameters
*
* @param ms - current timestamp in milliseconds in epoch milli
* @param minutesAfterMidnight - minutes after midnight
* @return calculates the start of day from given in ms plus minutes given in minutesAfterMidnight
*/
public static DateTimeType getEndOfChargeTime(long ms, long minutesAfterMidnight) {
// get today midnight
Instant timestamp = Instant.ofEpochMilli(ms);
ZonedDateTime zdt = timestamp.atZone(timeZoneProvider.getTimeZone());
ZonedDateTime endTime = zdt.withHour(0).withMinute(0).withSecond(0).plusMinutes(minutesAfterMidnight);
return new DateTimeType(endTime);
}
/**
* Get free port without other Thread interference from other AccountHandlers
*
* @return number of free port
*/
public static synchronized int getFreePort() {
while (PORTS.contains(port)) {
port++;
}
PORTS.add(port);
return port;
}
/**
* Register port for an AccountHandler
*/
public static synchronized void addPort(int portNr) {
if (PORTS.contains(portNr) && portNr != 99999) {
LOGGER.warn("Port {} already occupied", portNr);
}
PORTS.add(portNr);
}
/**
* Unregister port for an AccountHandler
*/
public static synchronized void removePort(int portNr) {
PORTS.remove(Integer.valueOf(portNr));
}
public static String getCallbackAddress(String callbackIP, int callbackPort) {
return "http://" + callbackIP + Constants.COLON + callbackPort + Constants.CALLBACK_ENDPOINT;
}
/**
* Calculate REST API server address according to region
*
* @param region - configured region
* @return base REST server address
*/
public static String getRestAPIServer(String region) {
switch (region) {
case Constants.REGION_APAC:
return Constants.REST_API_BASE_PA;
case Constants.REGION_CHINA:
return Constants.REST_API_BASE_CN;
case Constants.REGION_NORAM:
return Constants.REST_API_BASE_NA;
default:
return Constants.REST_API_BASE;
}
}
/**
* Calculate Login API server address according to region
*
* @param region - configured region
* @return base login server address
*/
public static String getLoginServer(String region) {
switch (region) {
case Constants.REGION_APAC:
return Constants.LOGIN_BASE_URI_PA;
case Constants.REGION_CHINA:
return Constants.LOGIN_BASE_URI_CN;
case Constants.REGION_NORAM:
return Constants.LOGIN_BASE_URI_NA;
default:
return Constants.LOGIN_BASE_URI;
}
}
/**
* Calculate websocket server address according to region
*
* @param region - configured region
* @return websocket base server address
*/
public static String getWebsocketServer(String region) {
switch (region) {
case Constants.REGION_APAC:
return Constants.WEBSOCKET_API_BASE_PA;
case Constants.REGION_CHINA:
return Constants.WEBSOCKET_API_BASE_CN;
case Constants.REGION_NORAM:
return Constants.WEBSOCKET_API_BASE_PA;
default:
return Constants.WEBSOCKET_API_BASE;
}
}
/**
* Calculate application name according to region
*
* @param region - configured region
* @return application name as String
*/
public static String getApplication(String region) {
switch (region) {
case Constants.REGION_APAC:
return Constants.X_APPLICATIONNAME_AP;
case Constants.REGION_CHINA:
return Constants.X_APPLICATIONNAME_CN;
case Constants.REGION_NORAM:
return Constants.X_APPLICATIONNAME_US;
default:
return Constants.X_APPLICATIONNAME;
}
}
/**
* Calculate application version according to region
*
* @param region - configured region
* @return application version as String
*/
public static String getRisApplicationVersion(String region) {
switch (region) {
case Constants.REGION_APAC:
return Constants.RIS_APPLICATION_VERSION_PA;
case Constants.REGION_CHINA:
return Constants.RIS_APPLICATION_VERSION_CN;
case Constants.REGION_NORAM:
return Constants.RIS_APPLICATION_VERSION_NA;
default:
return Constants.RIS_APPLICATION_VERSION;
}
}
/**
* Calculate user agent according to region
*
* @param region - configured region
* @return user agent as String
*/
public static String getUserAgent(String region) {
switch (region) {
case Constants.REGION_APAC:
return Constants.WEBSOCKET_USER_AGENT_PA;
case Constants.REGION_CHINA:
return Constants.WEBSOCKET_USER_AGENT_CN;
default:
return Constants.WEBSOCKET_USER_AGENT;
}
}
/**
* Calculate SDK version according to region
*
* @param region - configured region
* @return SDK version as String
*/
public static String getRisSDKVersion(String region) {
switch (region) {
case Constants.REGION_CHINA:
return Constants.RIS_SDK_VERSION_CN;
default:
return Constants.RIS_SDK_VERSION;
}
}
/**
* Calculate authorization config URL as pre-configuration prior to authorization call
*
* @param region - configured region
* @return authorization config URL as String
*/
public static String getAuthConfigURL(String region) {
return getRestAPIServer(region) + "/v1/config";
}
/**
* Calculate login app id according to region
*
* @param region - configured region
* @return login app id as String
*/
public static String getLoginAppId(String region) {
switch (region) {
case Constants.REGION_CHINA:
return Constants.LOGIN_APP_ID_CN;
default:
return Constants.LOGIN_APP_ID;
}
}
/**
* Calculate authorization URL for authorization call
*
* @param region - configured region
* @return authorization URL as String
*/
public static String getAuthURL(String region) {
return getRestAPIServer(region) + "/v1/login";
}
/**
* Calculate token URL for getting token
*
* @param region - configured region
* @return token URL as String
*/
public static String getTokenUrl(String region) {
return getLoginServer(region) + "/as/token.oauth2";
}
/**
* Decode String as Base64 from stored AccessTokenResponse
*
* @param token - Base64 String from storage
* @return AccessTokenResponse decoded from String, invalid token otherwise
*/
public static AccessTokenResponse fromString(String token) {
try {
byte[] data = Base64.getDecoder().decode(token);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
Object o = ois.readObject();
ois.close();
return (AccessTokenResponse) o;
} catch (IOException | ClassNotFoundException e) {
LOGGER.warn("Error converting string to token {}", e.getMessage());
}
return AuthService.INVALID_TOKEN;
}
/**
* Encode AccessTokenResponse as Base64 String for storage
*
* @param token - AccessTokenResponse to convert
*/
public static String toString(AccessTokenResponse token) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(token);
oos.close();
return Base64.getEncoder().encodeToString(baos.toByteArray());
} catch (IOException e) {
LOGGER.warn("Error converting token to string {}", e.getMessage());
}
return Constants.NOT_SET;
}
/**
* Combine vehicle data maps which is needed for partial updates.
* First take fullMap, then updates are taken from updateMap.
*
* @param fullMap - last present update of vehicle data
* @param updateMap - updates to override
* @return combined Map with updates taken into account
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Map combineMaps(Map fullMap, Map updateMap) {
final Map combined = new TreeMap();
fullMap.forEach((key, value) -> {
combined.put(key, value);
});
updateMap.forEach((key, value) -> {
combined.put(key, value);
});
return combined;
}
/**
* Converts a protobuf update into JSON String
*
* @param protoUpdate - proto update
* @param uid - thing type uid for identification
* @return JSON as String
*/
@SuppressWarnings({ "unused", "null" })
public static String proto2Json(VEPUpdate protoUpdate, ThingTypeUID uid) {
JSONObject protoJson = new JSONObject();
Map<String, VehicleAttributeStatus> m = protoUpdate.getAttributesMap();
m.forEach((key, value) -> {
Map<FieldDescriptor, Object> attMap = value.getAllFields();
JSONObject attributesJson = getJsonObject(attMap);
if (value.hasTemperaturePointsValue()) {
TemperaturePointsValue tpv = value.getTemperaturePointsValue();
JSONArray tmpPoints = new JSONArray();
List<VehicleEvents.TemperaturePoint> temperaturePointsList = tpv.getTemperaturePointsList();
temperaturePointsList.forEach(point -> {
JSONObject tmpPoint = getJsonObject(point.getAllFields());
tmpPoints.put(tmpPoint);
});
JSONObject points = new JSONObject();
points.put("temperature_points", tmpPoints);
attributesJson.put("temperature_points_value", points);
} else if (value.hasChargeProgramsValue()) {
ChargeProgramsValue cpv = value.getChargeProgramsValue();
JSONArray chargeProgramArray = new JSONArray();
List<ChargeProgramParameters> l = cpv.getChargeProgramParametersList();
l.forEach(cpp -> {
chargeProgramArray.put(getJsonObject(cpp.getAllFields()));
});
attributesJson.put("charge_programs_value", chargeProgramArray);
} else if (value.hasWeeklyProfileValue()) {
WeeklyProfileValue wpv = value.getWeeklyProfileValue();
JSONObject weeklyProfiles = new JSONObject();
List<VVRTimeProfile> timeProfilesList = wpv.getTimeProfilesList();
timeProfilesList.forEach(timeProfileProto -> {
JSONObject timeProfileJson = new JSONObject();
JSONArray days = new JSONArray(timeProfileProto.getDaysList());
timeProfileJson.put("days", days);
timeProfileJson.put("hour", timeProfileProto.getHour());
timeProfileJson.put("minute", timeProfileProto.getMinute());
timeProfileJson.put("active", timeProfileProto.getActive());
timeProfileJson.put("applicationIdentifier", timeProfileProto.getApplicationIdentifier());
weeklyProfiles.put(Integer.toString(timeProfileProto.getIdentifier()), timeProfileJson);
});
attributesJson.put("weekly_profile_value", weeklyProfiles);
} else if (value.hasWeeklySettingsHeadUnitValue()) {
WeeklySettingsHeadUnitValue wshuv = value.getWeeklySettingsHeadUnitValue();
List<WeeklySetting> valList = wshuv.getWeeklySettingsList();
JSONArray settingsJsonArray = new JSONArray();
valList.forEach(weeklySettingProto -> {
JSONObject settings = new JSONObject();
settings.put("day", weeklySettingProto.getDay());
settings.put("minutes_since_midnight", weeklySettingProto.getMinutesSinceMidnight());
settingsJsonArray.put(settings);
});
attributesJson.put("weekly_settings_head_unit_value", settingsJsonArray);
}
// check for errors - in fact JSONObject returns null in case of errors
if (attributesJson.toString() == null) {
LOGGER.trace("JSON conversion failed for Proto {}", key);
attributesJson = new JSONObject();
attributesJson.put(key, attMap.toString());
}
if (attributesJson.toString() == null) {
LOGGER.trace("JSON conversion failed for Map {}", key);
attributesJson = new JSONObject();
attributesJson.put(key, "Not supported by binding");
}
// Anonymize position
if ("positionLat".equals(key)) {
attributesJson.put("double_value", 1.23);
} else if ("positionLong".equals(key)) {
attributesJson.put("double_value", 4.56);
}
protoJson.put(key, attributesJson);
});
// finally put binding version in
JSONObject bindingInfo = new JSONObject();
bindingInfo.put("version", Constants.BINDING_VERSION);
bindingInfo.put("vehicle", uid.getAsString());
bindingInfo.put("oh-bundle", MercedesMeHandlerFactory.getVersion());
protoJson.put("bindingInfo", bindingInfo);
return protoJson.toString();
}
/**
* Converts a proto Map with FieldDescriptor into a JSON Object
*
* @param attMap - proto attributes Map
* @return JSONObject with key value pairs
*/
public static JSONObject getJsonObject(Map<FieldDescriptor, Object> attMap) {
JSONObject joa = new JSONObject();
attMap.forEach((aKey, aValue) -> {
String[] bKey = aKey.toString().split("\\.");
if (bKey.length > 1) {
joa.put(bKey[bKey.length - 1], aValue);
} else {
joa.put(bKey[0], aValue.toString());
}
});
return joa;
}
/**
* Calculate zone number from 3rdparty generated proto files
*
* @param zone - zone definition as String
* @return zone number for selection
*/
public static int getZoneNumber(String zone) {
if (ZONE_HASHMAP.isEmpty()) {
Zone[] zones = Zone.values();
for (int i = 0; i < zones.length - 1; i++) {
ZONE_HASHMAP.put(zones[i].name(), zones[i].getNumber());
}
}
Integer zoneNumber = ZONE_HASHMAP.get(zone);
if (zoneNumber != null) {
return zoneNumber;
}
return -1;
}
/**
* Calculate charge program number from 3rdparty generated proto files
*
* @param program - charge program definition as String
* @return charge program number for selection
*/
public static int getChargeProgramNumber(String program) {
if (PROGRAM_HASHMAP.isEmpty()) {
ChargeProgram[] programs = ChargeProgram.values();
for (int i = 0; i < programs.length - 1; i++) {
PROGRAM_HASHMAP.put(programs[i].name(), programs[i].getNumber());
}
}
Integer programNumber = PROGRAM_HASHMAP.get(program);
if (programNumber != null) {
return programNumber;
}
return -1;
}
/**
* Calculate duration String from given minutes
*
* @param durationMinutes - duration in minutes
* @return Sting in format days, hours and minutes
*/
public static String getDurationString(long durationMinutes) {
if (durationMinutes < 0) {
return "-1";
}
Duration duration = Duration.ofMinutes(durationMinutes);
if (duration.toDaysPart() > 0) {
return String.format("%sd %sh %sm", duration.toDaysPart(), duration.toHoursPart(),
duration.toMinutesPart());
} else if (duration.toHoursPart() > 0) {
return String.format("%sh %sm", duration.toHoursPart(), duration.toMinutesPart());
} else {
return String.format("%sm", duration.toMinutesPart());
}
}
/**
* Get int from proto VehicleAttributeStatus
*
* @param value - proto value
* @return value as int, -1 otherwise
*/
public static int getInt(VehicleAttributeStatus value) {
return Double.valueOf(getDouble(value)).intValue();
}
/**
* Get double from proto VehicleAttributeStatus
*
* @param value - proto value
* @return value as double, -1 otherwise
*/
public static double getDouble(@Nullable VehicleAttributeStatus value) {
double ret = -1;
if (value != null) {
if (!isNil(value)) {
if (value.getDisplayValue() != null) {
if (value.getDisplayValue().strip().length() > 0) {
try {
return Double.valueOf(value.getDisplayValue());
} catch (NumberFormatException nfe) {
LOGGER.trace("Cannot transform Display Value {} into Double", value.getDisplayValue());
}
}
}
if (value.hasDoubleValue()) {
return value.getDoubleValue();
} else if (value.hasIntValue()) {
return value.getIntValue();
}
}
}
return ret;
}
/**
* Checks proto VehicleAttributeStatus is nil
*
* @param value - proto value
* @return true if nil value is present, false otherwise
*/
public static boolean isNil(@Nullable VehicleAttributeStatus value) {
if (value != null) {
if (value.hasNilValue()) {
return value.getNilValue();
}
}
return false;
}
/**
* Get country code from configured LocaleProvider
*
* @return country code
*/
public static String getCountry() {
return localeProvider.getLocale().getCountry();
}
/**
* Calculate distance between two points in latitude and longitude taking
* into account height difference. If you are not interested in height
* difference pass 0.0. Uses Haversine method as its base.
*
* https://stackoverflow.com/questions/3694380/calculating-distance-between-two-points-using-latitude-longitude
* lat1, lon1 Start point lat2, lon2 End point el1 Start altitude in meters
* el2 End altitude in meters
*
* @returns Distance in Meters
*/
public static double distance(double lat1, double lat2, double lon1, double lon2, double el1, double el2) {
double latDistance = Math.toRadians(lat2 - lat1);
double lonDistance = Math.toRadians(lon2 - lon1);
double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) + Math.cos(Math.toRadians(lat1))
* Math.cos(Math.toRadians(lat2)) * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = R * c * 1000; // convert to meters
double height = el1 - el2;
distance = Math.pow(distance, 2) + Math.pow(height, 2);
return Math.sqrt(distance);
}
/**
* Calculates a list of CommandOptions for temperature settings which are also available in the Mercedes Me app
*
* @param unit - unit of temperature
* @return List of CommandOptions, empty if unit isn't supported
*/
public static List<CommandOption> getTemperatureOptions(Unit<?> unit) {
if (ImperialUnits.FAHRENHEIT.equals(unit)) {
if (FAHRENHEIT_COMMAND_OPTIONS.isEmpty()) {
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("60 °F", "60 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("61 °F", "61 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("62 °F", "62 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("63 °F", "63 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("64 °F", "64 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("65 °F", "65 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("66 °F", "66 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("67 °F", "67 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("68 °F", "68 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("69 °F", "69 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("70 °F", "70 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("71 °F", "71 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("72 °F", "72 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("73 °F", "73 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("74 °F", "74 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("75 °F", "75 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("76 °F", "76 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("77 °F", "77 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("78 °F", "78 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("79 °F", "79 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("80 °F", "80 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("81 °F", "81 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("82 °F", "82 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("83 °F", "83 °F"));
FAHRENHEIT_COMMAND_OPTIONS.add(new CommandOption("84 °F", "84 °F"));
}
return FAHRENHEIT_COMMAND_OPTIONS;
} else if (SIUnits.CELSIUS.equals(unit)) {
if (CELSIUS_COMMAND_OPTIONS.isEmpty()) {
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("16 °C", "16 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("16.5 °C", "16.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("17 °C", "17 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("17.5 °C", "17.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("18 °C", "18 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("18.5 °C", "18.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("19 °C", "19 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("19.5 °C", "19.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("20 °C", "20 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("20.5 °C", "20.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("21 °C", "21 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("21.5 °C", "21.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("22 °C", "22 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("22.5 °C", "22.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("23 °C", "23 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("23.5 °C", "23.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("24 °C", "24 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("24.5 °C", "24.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("25 °C", "25 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("25.5 °C", "25.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("26 °C", "26 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("26.5 °C", "26.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("27 °C", "27 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("27.5 °C", "27.5 °C"));
CELSIUS_COMMAND_OPTIONS.add(new CommandOption("28 °C", "28 °C"));
}
return CELSIUS_COMMAND_OPTIONS;
} else {
return new ArrayList<CommandOption>();
}
}
}

View File

@ -8,50 +8,9 @@
<parameter name="vin" type="text" required="true">
<label>Vehicle Identification Number</label>
</parameter>
<parameter name="refreshInterval" type="integer" min="1" unit="min" required="true">
<label>Refresh Interval</label>
<description>Data refresh rate for vehicle data</description>
<default>5</default>
</parameter>
<parameter name="batteryCapacity" type="decimal">
<label>Battery Capacity</label>
<description>Battery capacity in kWh of vehicle</description>
</parameter>
<!-- https://developer.mercedes-benz.com/products/vehicle_images/docs#_default_image_settings -->
<parameter name="background" type="boolean">
<label>Background Image</label>
<description>Vehicle images provided with or without background</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="night" type="boolean">
<label>Night Image</label>
<description>Vehicle images in night conditions</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="cropped" type="boolean">
<label>Cropped Image</label>
<description>Vehicle images in 4:3 instead of 16:9</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="roofOpen" type="boolean">
<label>Cabriolet Open Roof</label>
<description>Vehicle images with open roof (only Cabriolet)</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="format" type="text">
<label>Image Format</label>
<description>Preferred Image Format</description>
<default>webp</default>
<advanced>true</advanced>
<options>
<option value="webp">webp</option>
<option value="png">png</option>
<option value="jpeg">jpeg</option>
</options>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@ -5,42 +5,29 @@
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:mercedesme:bridge">
<parameter name="clientId" type="text" required="true">
<label>MB Developer Client ID</label>
<description>Mercedes Benz Developer Client ID</description>
<parameter name="email" type="text" required="true">
<label>MercedesMe EMail</label>
<description>EMail address for MercedesMe account</description>
<context>email</context>
</parameter>
<parameter name="clientSecret" type="text" required="true">
<label>MB Developer Client Secret</label>
<description>Mercedes Benz Developer Client Secret</description>
<parameter name="region" type="text" required="true">
<label>Region</label>
<options>
<option value="EU">Europe</option>
<option value="NA">North America</option>
<option value="AP">Asia-Pacific</option>
<option value="CN">China</option>
</options>
</parameter>
<parameter name="imageApiKey" type="text">
<label>MB Developer Image API Key</label>
<description>Mercedes Benz Developer Image API Key</description>
<parameter name="pin" type="text" required="false">
<label>PIN</label>
<description>PIN for commands</description>
<context>password</context>
</parameter>
<parameter name="odoScope" type="boolean">
<label>PayAsYourDrive Insurance</label>
<description>Provides total Mileage</description>
<default>true</default>
</parameter>
<parameter name="vehicleScope" type="boolean">
<label>Vehicle Status</label>
<description>Status of doors, windows lights</description>
<default>true</default>
</parameter>
<parameter name="lockScope" type="boolean">
<label>Vehicle Lock Status</label>
<description>Lock status of doors and trunk</description>
<default>true</default>
</parameter>
<parameter name="fuelScope" type="boolean">
<label>Fuel Status</label>
<description>Tank level and range</description>
<default>true</default>
</parameter>
<parameter name="evScope" type="boolean">
<label>Electric Vehicle Status</label>
<description>Electric charge and range</description>
<default>true</default>
<parameter name="refreshInterval" type="integer" min="5" unit="min" required="true">
<label>Refresh Interval</label>
<description>Refresh Interval in Minutes</description>
<default>15</default>
</parameter>
<parameter name="callbackIP" type="text">
<label>Callback IP Address</label>

View File

@ -8,50 +8,9 @@
<parameter name="vin" type="text" required="true">
<label>Vehicle Identification Number</label>
</parameter>
<parameter name="refreshInterval" type="integer" min="1" unit="min" required="true">
<label>Refresh Interval</label>
<description>Data refresh rate for your vehicle data</description>
<default>5</default>
</parameter>
<parameter name="fuelCapacity" type="decimal" min="0">
<label>Fuel Capacity</label>
<description>Fuel capacity in liters of vehicle</description>
</parameter>
<!-- https://developer.mercedes-benz.com/products/vehicle_images/docs#_default_image_settings -->
<parameter name="background" type="boolean">
<label>Background Image</label>
<description>Vehicle images provided with or without background</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="night" type="boolean">
<label>Night Image</label>
<description>Vehicle images in night conditions</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="cropped" type="boolean">
<label>Cropped Image</label>
<description>Vehicle images in 4:3 instead of 16:9</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="roofOpen" type="boolean">
<label>Cabriolet Open Roof</label>
<description>Vehicle images with open roof (only Cabriolet)</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="format" type="text">
<label>Image Format</label>
<description>Preferred Image Format</description>
<default>webp</default>
<advanced>true</advanced>
<options>
<option value="webp">webp</option>
<option value="png">png</option>
<option value="jpeg">jpeg</option>
</options>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@ -8,11 +8,6 @@
<parameter name="vin" type="text" required="true">
<label>Vehicle Identification Number</label>
</parameter>
<parameter name="refreshInterval" type="integer" min="1" unit="min" required="true">
<label>Refresh Interval</label>
<description>Data refresh rate for vehicle data</description>
<default>5</default>
</parameter>
<parameter name="batteryCapacity" type="decimal">
<label>Battery Capacity</label>
<description>Battery capacity in kWh of vehicle</description>
@ -21,41 +16,5 @@
<label>Fuel Capacity</label>
<description>Fuel capacity in liters of vehicle</description>
</parameter>
<!-- https://developer.mercedes-benz.com/products/vehicle_images/docs#_default_image_settings -->
<parameter name="background" type="boolean">
<label>Background Image</label>
<description>Vehicle images provided with or without background</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="night" type="boolean">
<label>Night Image</label>
<description>Vehicle images in night conditions</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="cropped" type="boolean">
<label>Cropped Image</label>
<description>Vehicle images in 4:3 instead of 16:9</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="roofOpen" type="boolean">
<label>Cabriolet Open Roof</label>
<description>Vehicle images with open roof (only Cabriolet)</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="format" type="text">
<label>Image Format</label>
<description>Preferred Image Format</description>
<default>webp</default>
<advanced>true</advanced>
<options>
<option value="webp">webp</option>
<option value="png">png</option>
<option value="jpeg">jpeg</option>
</options>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@ -16,203 +16,300 @@ thing-type.mercedesme.hybrid.description = Conventional Fuel Vehicle with suppor
# thing types config
thing-type.config.mercedesme.bev.background.label = Background Image
thing-type.config.mercedesme.bev.background.description = Vehicle images provided with or without background
thing-type.config.mercedesme.bev.batteryCapacity.label = Battery Capacity
thing-type.config.mercedesme.bev.batteryCapacity.description = Battery capacity in kwh of vehicle
thing-type.config.mercedesme.bev.cropped.label = Cropped Image
thing-type.config.mercedesme.bev.cropped.description = Vehicle images in 4:3 instead of 16:9
thing-type.config.mercedesme.bev.format.label = Image Format
thing-type.config.mercedesme.bev.format.description = Preferred Image Format
thing-type.config.mercedesme.bev.format.option.webp = webp
thing-type.config.mercedesme.bev.format.option.png = png
thing-type.config.mercedesme.bev.format.option.jpeg = jpeg
thing-type.config.mercedesme.bev.night.label = Night Image
thing-type.config.mercedesme.bev.night.description = Vehicle images in night conditions
thing-type.config.mercedesme.bev.refreshInterval.label = Refresh Interval
thing-type.config.mercedesme.bev.refreshInterval.description = Data refresh rate for vehicle data
thing-type.config.mercedesme.bev.roofOpen.label = Cabriolet Open Roof
thing-type.config.mercedesme.bev.roofOpen.description = Vehicle images with open roof (only Cabriolet)
thing-type.config.mercedesme.bev.batteryCapacity.description = Battery capacity in kWh of vehicle
thing-type.config.mercedesme.bev.vin.label = Vehicle Identification Number
thing-type.config.mercedesme.bridge.callbackIP.label = Callback IP Address
thing-type.config.mercedesme.bridge.callbackIP.description = IP address for openHAB callback URL
thing-type.config.mercedesme.bridge.callbackPort.label = Callback Port Number
thing-type.config.mercedesme.bridge.callbackPort.description = Port Number for openHAB callback URL
thing-type.config.mercedesme.bridge.clientId.label = MB Developer Client ID
thing-type.config.mercedesme.bridge.clientId.description = Mercedes Benz Developer Client ID
thing-type.config.mercedesme.bridge.clientSecret.label = MB Developer Client Secret
thing-type.config.mercedesme.bridge.clientSecret.description = Mercedes Benz Developer Client Secret
thing-type.config.mercedesme.bridge.evScope.label = Electric Vehicle Status
thing-type.config.mercedesme.bridge.evScope.description = Electric charge and range
thing-type.config.mercedesme.bridge.fuelScope.label = Fuel Status
thing-type.config.mercedesme.bridge.fuelScope.description = Tank level and range
thing-type.config.mercedesme.bridge.imageApiKey.label = MB Developer Image API Key
thing-type.config.mercedesme.bridge.imageApiKey.description = Mercedes Benz Developer Image API Key
thing-type.config.mercedesme.bridge.lockScope.label = Vehicle Lock Status
thing-type.config.mercedesme.bridge.lockScope.description = Lock status of doors and trunk
thing-type.config.mercedesme.bridge.odoScope.label = PayAsYourDrive Insurance
thing-type.config.mercedesme.bridge.odoScope.description = Provides total Mileage
thing-type.config.mercedesme.bridge.vehicleScope.label = Vehicle Status
thing-type.config.mercedesme.bridge.vehicleScope.description = Status of doors, windows lights
thing-type.config.mercedesme.conv.background.label = Background Image
thing-type.config.mercedesme.conv.background.description = Vehicle images provided with or without background
thing-type.config.mercedesme.conv.cropped.label = Cropped Image
thing-type.config.mercedesme.conv.cropped.description = Vehicle images in 4:3 instead of 16:9
thing-type.config.mercedesme.conv.format.label = Image Format
thing-type.config.mercedesme.conv.format.description = Preferred Image Format
thing-type.config.mercedesme.conv.format.option.webp = webp
thing-type.config.mercedesme.conv.format.option.png = png
thing-type.config.mercedesme.conv.format.option.jpeg = jpeg
thing-type.config.mercedesme.bridge.email.label = MercedesMe EMail
thing-type.config.mercedesme.bridge.email.description = EMail address for MercedesMe account
thing-type.config.mercedesme.bridge.pin.label = PIN
thing-type.config.mercedesme.bridge.pin.description = PIN for commands
thing-type.config.mercedesme.bridge.refreshInterval.label = Refresh Interval
thing-type.config.mercedesme.bridge.refreshInterval.description = Refresh Interval in Minutes
thing-type.config.mercedesme.bridge.region.label = Region
thing-type.config.mercedesme.bridge.region.option.EU = Europe
thing-type.config.mercedesme.bridge.region.option.NA = North America
thing-type.config.mercedesme.bridge.region.option.AP = Asia-Pacific
thing-type.config.mercedesme.bridge.region.option.CN = China
thing-type.config.mercedesme.conv.fuelCapacity.label = Fuel Capacity
thing-type.config.mercedesme.conv.fuelCapacity.description = Fuel capacity in liters of vehicle
thing-type.config.mercedesme.conv.night.label = Night Image
thing-type.config.mercedesme.conv.night.description = Vehicle images in night conditions
thing-type.config.mercedesme.conv.refreshInterval.label = Refresh Interval
thing-type.config.mercedesme.conv.refreshInterval.description = Data refresh rate for your vehicle data
thing-type.config.mercedesme.conv.roofOpen.label = Cabriolet Open Roof
thing-type.config.mercedesme.conv.roofOpen.description = Vehicle images with open roof (only Cabriolet)
thing-type.config.mercedesme.conv.vin.label = Vehicle Identification Number
thing-type.config.mercedesme.hybrid.background.label = Background Image
thing-type.config.mercedesme.hybrid.background.description = Vehicle images provided with or without background
thing-type.config.mercedesme.hybrid.batteryCapacity.label = Battery Capacity
thing-type.config.mercedesme.hybrid.batteryCapacity.description = Battery capacity in kwh of vehicle
thing-type.config.mercedesme.hybrid.cropped.label = Cropped Image
thing-type.config.mercedesme.hybrid.cropped.description = Vehicle images in 4:3 instead of 16:9
thing-type.config.mercedesme.hybrid.format.label = Image Format
thing-type.config.mercedesme.hybrid.format.description = Preferred Image Format
thing-type.config.mercedesme.hybrid.format.option.webp = webp
thing-type.config.mercedesme.hybrid.format.option.png = png
thing-type.config.mercedesme.hybrid.format.option.jpeg = jpeg
thing-type.config.mercedesme.hybrid.batteryCapacity.description = Battery capacity in kWh of vehicle
thing-type.config.mercedesme.hybrid.fuelCapacity.label = Fuel Capacity
thing-type.config.mercedesme.hybrid.fuelCapacity.description = Fuel capacity in liters of vehicle
thing-type.config.mercedesme.hybrid.night.label = Night Image
thing-type.config.mercedesme.hybrid.night.description = Vehicle images in night conditions
thing-type.config.mercedesme.hybrid.refreshInterval.label = Refresh Interval
thing-type.config.mercedesme.hybrid.refreshInterval.description = Data refresh rate for vehicle data
thing-type.config.mercedesme.hybrid.roofOpen.label = Cabriolet Open Roof
thing-type.config.mercedesme.hybrid.roofOpen.description = Vehicle images with open roof (only Cabriolet)
thing-type.config.mercedesme.hybrid.vin.label = Vehicle Identification Number
# channel group types
channel-group-type.mercedesme.charge-values.label = Charging Data
channel-group-type.mercedesme.command-values.label = Status of Last Command
channel-group-type.mercedesme.door-values.label = Detailed Door Status
channel-group-type.mercedesme.door-values.description = Detailed Status of all Doors and Windows
channel-group-type.mercedesme.image-values.label = Vehicle Images
channel-group-type.mercedesme.light-values.label = Light Status
channel-group-type.mercedesme.light-values.description = Light Status of interior lights and main light switch
channel-group-type.mercedesme.location-values.label = Vehicle Location
channel-group-type.mercedesme.location-values.description = Heading of vehicle
channel-group-type.mercedesme.hvac-values.label = Vehicle Climate Control
channel-group-type.mercedesme.lock-ev-values.label = Lock Status
channel-group-type.mercedesme.lock-ev-values.description = Door Lock Status
channel-group-type.mercedesme.lock-values.label = Lock Status
channel-group-type.mercedesme.lock-values.description = Vehicle Lock Status
channel-group-type.mercedesme.lock-values.description = Door Lock Status
channel-group-type.mercedesme.position-values.label = Vehicle Location
channel-group-type.mercedesme.position-values.description = Heading of vehicle
channel-group-type.mercedesme.range-conv-values.label = Range and Fuel Data
channel-group-type.mercedesme.range-conv-values.description = Provides Mileage, remaining range and fuel level values
channel-group-type.mercedesme.range-ev-values.label = Range and Charge Data
channel-group-type.mercedesme.range-ev-values.label = Range and Battery Data
channel-group-type.mercedesme.range-ev-values.description = Provides Mileage, remaining range and charge level values
channel-group-type.mercedesme.range-hybrid-values.label = Range, Charge / Fuel Data
channel-group-type.mercedesme.range-hybrid-values.label = Range, Battery and Fuel Data
channel-group-type.mercedesme.range-hybrid-values.description = Provides mileage, remaining fuel and range data for hybrid vehicles
channel-group-type.mercedesme.service-ev-values.label = Service and Warnings
channel-group-type.mercedesme.service-values.label = Warnings
channel-group-type.mercedesme.service-values.description = Current active warnings
channel-group-type.mercedesme.tires-values.label = Tire Status
channel-group-type.mercedesme.tires-values.description = Tire Pressure Values
channel-group-type.mercedesme.trip-conv-values.label = Last Trip Data
channel-group-type.mercedesme.trip-ev-values.label = Last Trip Data
channel-group-type.mercedesme.trip-hybrid-values.label = Last Trip Data
channel-group-type.mercedesme.vehicle-values.label = Vehicle Status
channel-group-type.mercedesme.vehicle-values.description = General vehicle status data
channel-group-type.mercedesme.window-values.label = Detailed Window Status
channel-group-type.mercedesme.window-values.description = Detailed Status Windows
# channel types
channel-type.mercedesme.charged-channel.label = Charged Battery Energy
channel-type.mercedesme.clear-cache-channel.label = Remove All Stored Images
channel-type.mercedesme.deck-lid-channel.label = Deck Lid
channel-type.mercedesme.deck-lid-lock-channel.label = Deck Lid Lock
channel-type.mercedesme.doors-lock-channel.label = Door Lock Status
channel-type.mercedesme.doors-lock-channel.state.option.0 = Unlocked
channel-type.mercedesme.doors-lock-channel.state.option.1 = Locked Internal
channel-type.mercedesme.doors-lock-channel.state.option.2 = Locked External
channel-type.mercedesme.doors-lock-channel.state.option.3 = Unlocked Selective
channel-type.mercedesme.driver-front-channel.label = Driver Door
channel-type.mercedesme.driver-rear-channel.label = Driver Door Rear
channel-type.mercedesme.flap-lock-channel.label = Flap Lock
channel-type.mercedesme.fuel-level-channel.label = Fuel Level
channel-type.mercedesme.fuel-open-channel.label = Open Fuel Capacity
channel-type.mercedesme.fuel-remain-channel.label = Remaining Fuel
channel-type.mercedesme.heading-channel.label = Heading Angle
channel-type.mercedesme.image-data-channel.label = Rendered Vehicle Image
channel-type.mercedesme.image-view-channel.label = Image Viewport
channel-type.mercedesme.interior-front-channel.label = Interior Light Front
channel-type.mercedesme.interior-rear-channel.label = Interior Light Rear
channel-type.mercedesme.last-doors-update-channel.label = Last Doors Update
channel-type.mercedesme.last-doors-update-channel.state.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
channel-type.mercedesme.last-lights-update-channel.label = Last Light Update
channel-type.mercedesme.last-lights-update-channel.state.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
channel-type.mercedesme.last-location-update-channel.label = Last Location Update
channel-type.mercedesme.last-location-update-channel.state.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
channel-type.mercedesme.last-lock-update-channel.label = Last Lock Update
channel-type.mercedesme.last-lock-update-channel.state.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
channel-type.mercedesme.last-range-update-channel.label = Last Range Update
channel-type.mercedesme.last-range-update-channel.state.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
channel-type.mercedesme.last-windows-update-channel.label = Last Window Update
channel-type.mercedesme.last-windows-update-channel.state.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
channel-type.mercedesme.light-switch-channel.label = Main Light Rotary
channel-type.mercedesme.light-switch-channel.state.option.0 = Auto
channel-type.mercedesme.light-switch-channel.state.option.1 = Headlight
channel-type.mercedesme.light-switch-channel.state.option.2 = Sidelight Left
channel-type.mercedesme.light-switch-channel.state.option.3 = Sidelight Right
channel-type.mercedesme.light-switch-channel.state.option.4 = Parking Light
channel-type.mercedesme.mileage-channel.label = Mileage
channel-type.mercedesme.passenger-front-channel.label = Passenger Door
channel-type.mercedesme.passenger-rear-channel.label = Passenger Door Rear
channel-type.mercedesme.radius-electric-channel.label = Electric Radius
channel-type.mercedesme.radius-fuel-channel.label = Fuel Radius
channel-type.mercedesme.radius-hybrid-channel.label = Hybrid Radius
channel-type.mercedesme.range-electric-channel.label = Electric Range
channel-type.mercedesme.range-fuel-channel.label = Fuel Range
channel-type.mercedesme.range-hybrid-channel.label = Hybrid Range
channel-type.mercedesme.reading-left-channel.label = Reading Light Left
channel-type.mercedesme.reading-right-channel.label = Reading Light Right
channel-type.mercedesme.rooftop-channel.label = Roof top
channel-type.mercedesme.rooftop-channel.state.option.0 = Unlocked
channel-type.mercedesme.rooftop-channel.state.option.1 = Open and locked
channel-type.mercedesme.rooftop-channel.state.option.2 = Closed and locked
channel-type.mercedesme.soc-channel.label = Battery Charge Level
channel-type.mercedesme.sunroof-channel.label = Sun Roof
channel-type.mercedesme.sunroof-channel.state.option.0 = Closed
channel-type.mercedesme.sunroof-channel.state.option.1 = Open
channel-type.mercedesme.sunroof-channel.state.option.2 = Open Lifting
channel-type.mercedesme.sunroof-channel.state.option.3 = Running
channel-type.mercedesme.sunroof-channel.state.option.4 = Closing
channel-type.mercedesme.sunroof-channel.state.option.5 = Opening
channel-type.mercedesme.sunroof-channel.state.option.6 = Closing
channel-type.mercedesme.uncharged-channel.label = Uncharged Battery Energy
channel-type.mercedesme.window-driver-front-channel.label = Driver Window
channel-type.mercedesme.window-driver-front-channel.state.option.0 = Intermediate
channel-type.mercedesme.window-driver-front-channel.state.option.1 = Open
channel-type.mercedesme.window-driver-front-channel.state.option.2 = Closed
channel-type.mercedesme.window-driver-front-channel.state.option.3 = Airing
channel-type.mercedesme.window-driver-front-channel.state.option.4 = Intermediate
channel-type.mercedesme.window-driver-front-channel.state.option.5 = Running
channel-type.mercedesme.window-driver-rear-channel.label = Driver Window Rear
channel-type.mercedesme.window-driver-rear-channel.state.option.0 = Intermediate
channel-type.mercedesme.window-driver-rear-channel.state.option.1 = Open
channel-type.mercedesme.window-driver-rear-channel.state.option.2 = Closed
channel-type.mercedesme.window-driver-rear-channel.state.option.3 = Airing
channel-type.mercedesme.window-driver-rear-channel.state.option.4 = Intermediate
channel-type.mercedesme.window-driver-rear-channel.state.option.5 = Running
channel-type.mercedesme.window-passenger-front-channel.label = Passenger Window
channel-type.mercedesme.window-passenger-front-channel.state.option.0 = Intermediate
channel-type.mercedesme.window-passenger-front-channel.state.option.1 = Open
channel-type.mercedesme.window-passenger-front-channel.state.option.2 = Closed
channel-type.mercedesme.window-passenger-front-channel.state.option.3 = Airing
channel-type.mercedesme.window-passenger-front-channel.state.option.4 = Intermediate
channel-type.mercedesme.window-passenger-front-channel.state.option.5 = Running
channel-type.mercedesme.window-passenger-rear-channel.label = Passenger Window Rear
channel-type.mercedesme.window-passenger-rear-channel.state.option.0 = Intermediate
channel-type.mercedesme.window-passenger-rear-channel.state.option.1 = Open
channel-type.mercedesme.window-passenger-rear-channel.state.option.2 = Closed
channel-type.mercedesme.window-passenger-rear-channel.state.option.3 = Airing
channel-type.mercedesme.window-passenger-rear-channel.state.option.4 = Intermediate
channel-type.mercedesme.window-passenger-rear-channel.state.option.5 = Running
channel-type.mercedesme.active-hvac.label = AC Control
channel-type.mercedesme.active.label = Charge Active
channel-type.mercedesme.auto-unlock.label = Coupler Auto Unlock
channel-type.mercedesme.auto-unlock.description = Auto unlock coupler after charging
channel-type.mercedesme.aux-heat.label = Auxiliary Heating Control
channel-type.mercedesme.avg-speed-reset.label = Rst Average Speed
channel-type.mercedesme.avg-speed-reset.description = Average speed since last Reset
channel-type.mercedesme.avg-speed.label = Trip Average Speed
channel-type.mercedesme.brake-fluid.label = Brake Fluid
channel-type.mercedesme.brake-lining-wear.label = Brake Lining Wear
channel-type.mercedesme.charge-flap.label = Charge Flap
channel-type.mercedesme.charge-flap.description = Charge Flap Status
channel-type.mercedesme.charge-flap.state.option.0 = Open
channel-type.mercedesme.charge-flap.state.option.1 = Closed
channel-type.mercedesme.charged.label = Charged Battery Energy
channel-type.mercedesme.cmd-last-update.label = Command Updated
channel-type.mercedesme.cmd-last-update.state.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
channel-type.mercedesme.cmd-name.label = Command Name
channel-type.mercedesme.cmd-state.label = Command State
channel-type.mercedesme.command-capabilities.label = Command Capabilities
channel-type.mercedesme.consumption-conv-reset.label = Avg Cons Rst Fuel
channel-type.mercedesme.consumption-conv-reset.description = Average consumption since last reset fuel
channel-type.mercedesme.consumption-conv-unit.label = Avg Cons Unit Fuel
channel-type.mercedesme.consumption-conv-unit.description = Average consumption unit fuel
channel-type.mercedesme.consumption-conv.label = Trip Avg Cons Fuel
channel-type.mercedesme.consumption-conv.description = Last Trip Average Consumption Fuel
channel-type.mercedesme.consumption-ev-reset.label = Avg Cons Rst EV
channel-type.mercedesme.consumption-ev-reset.description = Average consumption since last reset electric
channel-type.mercedesme.consumption-ev-unit.label = Avg Cons Unit EV
channel-type.mercedesme.consumption-ev-unit.description = Average consumption unit electric
channel-type.mercedesme.consumption-ev.label = Trip Avg Cons EV
channel-type.mercedesme.consumption-ev.description = Last Trip Average Consumption Electric
channel-type.mercedesme.coolant-fluid.label = Coolant Fluid Low
channel-type.mercedesme.coupler-ac.label = Charge Coupler AC
channel-type.mercedesme.coupler-ac.description = Coupler AC Status
channel-type.mercedesme.coupler-ac.state.option.0 = Plugged
channel-type.mercedesme.coupler-ac.state.option.2 = Unplugged
channel-type.mercedesme.coupler-dc.label = Charge Coupler DC
channel-type.mercedesme.coupler-dc.description = Coupler DC Status
channel-type.mercedesme.coupler-dc.state.option.0 = Plugged
channel-type.mercedesme.coupler-dc.state.option.2 = Unplugged
channel-type.mercedesme.coupler-lock.label = Charge Coupler Lock
channel-type.mercedesme.coupler-lock.description = Coupler Lock Status
channel-type.mercedesme.coupler-lock.state.option.0 = Locked
channel-type.mercedesme.coupler-lock.state.option.1 = Unlocked
channel-type.mercedesme.deck-lid-lock.label = Deck Lid Lock
channel-type.mercedesme.deck-lid.label = Deck Lid
channel-type.mercedesme.distance-reset.label = Rst Distance
channel-type.mercedesme.distance-reset.description = Distance since last Reset
channel-type.mercedesme.distance.label = Trip Distance
channel-type.mercedesme.door-status.label = Door Status
channel-type.mercedesme.door-status.state.option.0 = Open
channel-type.mercedesme.door-status.state.option.1 = Closed
channel-type.mercedesme.driven-time-reset.label = Rst Driving Time
channel-type.mercedesme.driven-time-reset.description = Driving time since last Reset
channel-type.mercedesme.driven-time.label = Trip String Time
channel-type.mercedesme.end-time.label = Charge End Estimation
channel-type.mercedesme.end-time.state.pattern = %1$tH:%1$tM
channel-type.mercedesme.engine-hood.label = Engine Hood
channel-type.mercedesme.engine.label = Engine Warning
channel-type.mercedesme.feature-capabilities.label = Feature Capabilities
channel-type.mercedesme.flip-window.label = Flip Window
channel-type.mercedesme.front-left-hvac.label = AC Seat Front Left
channel-type.mercedesme.front-left-lock.label = Door Lock Front Left
channel-type.mercedesme.front-left-window.label = Window Front Left
channel-type.mercedesme.front-left-window.state.option.0 = Intermediate
channel-type.mercedesme.front-left-window.state.option.1 = Open
channel-type.mercedesme.front-left-window.state.option.2 = Closed
channel-type.mercedesme.front-left-window.state.option.3 = Airing
channel-type.mercedesme.front-left-window.state.option.4 = Intermediate
channel-type.mercedesme.front-left-window.state.option.5 = Running
channel-type.mercedesme.front-left.label = Door Front Left
channel-type.mercedesme.front-right-hvac.label = AC Seat Front Right
channel-type.mercedesme.front-right-lock.label = Door Lock Front Right
channel-type.mercedesme.front-right-window.label = Window Front Right
channel-type.mercedesme.front-right-window.state.option.0 = Intermediate
channel-type.mercedesme.front-right-window.state.option.1 = Open
channel-type.mercedesme.front-right-window.state.option.2 = Closed
channel-type.mercedesme.front-right-window.state.option.3 = Airing
channel-type.mercedesme.front-right-window.state.option.4 = Intermediate
channel-type.mercedesme.front-right-window.state.option.5 = Running
channel-type.mercedesme.front-right.label = Door Front Right
channel-type.mercedesme.fuel-level.label = Fuel Level
channel-type.mercedesme.fuel-open.label = Open Fuel Capacity
channel-type.mercedesme.fuel-remain.label = Remaining Fuel
channel-type.mercedesme.gas-flap-lock.label = Flap Lock
channel-type.mercedesme.gps.label = Position GPS
channel-type.mercedesme.heading.label = Position Heading Angle
channel-type.mercedesme.home-distance.label = Distance to Home
channel-type.mercedesme.ignition.label = Ignition
channel-type.mercedesme.ignition.state.option.0 = Lock
channel-type.mercedesme.ignition.state.option.1 = Off
channel-type.mercedesme.ignition.state.option.2 = Accessory
channel-type.mercedesme.ignition.state.option.4 = On
channel-type.mercedesme.ignition.state.option.5 = Start
channel-type.mercedesme.ignition.command.option.0 = Off
channel-type.mercedesme.ignition.command.option.4 = On
channel-type.mercedesme.last-update.label = Tire Update Time
channel-type.mercedesme.last-update.state.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
channel-type.mercedesme.lock.label = Lock Status/Control
channel-type.mercedesme.lock.state.option.0 = Locked
channel-type.mercedesme.lock.state.option.1 = Unlocked
channel-type.mercedesme.lock.command.option.0 = Lock
channel-type.mercedesme.lock.command.option.1 = Unlock
channel-type.mercedesme.marker-front-left.label = Tire Warning Front Left
channel-type.mercedesme.marker-front-left.state.option.0 = No warning
channel-type.mercedesme.marker-front-left.state.option.1 = Soft warning
channel-type.mercedesme.marker-front-left.state.option.2 = Low warning
channel-type.mercedesme.marker-front-left.state.option.3 = Deflation
channel-type.mercedesme.marker-front-left.state.option.4 = Unknown warning
channel-type.mercedesme.marker-front-right.label = Tire Warning Front Right
channel-type.mercedesme.marker-front-right.state.option.0 = No warning
channel-type.mercedesme.marker-front-right.state.option.1 = Soft warning
channel-type.mercedesme.marker-front-right.state.option.2 = Low warning
channel-type.mercedesme.marker-front-right.state.option.3 = Deflation
channel-type.mercedesme.marker-front-right.state.option.4 = Unknown warning
channel-type.mercedesme.marker-rear-left.label = Tire Warning Rear Left
channel-type.mercedesme.marker-rear-left.state.option.0 = No warning
channel-type.mercedesme.marker-rear-left.state.option.1 = Soft warning
channel-type.mercedesme.marker-rear-left.state.option.2 = Low warning
channel-type.mercedesme.marker-rear-left.state.option.3 = Deflation
channel-type.mercedesme.marker-rear-left.state.option.4 = Unknown warning
channel-type.mercedesme.marker-rear-right.label = Tire Warning Rear Right
channel-type.mercedesme.marker-rear-right.state.option.0 = No warning
channel-type.mercedesme.marker-rear-right.state.option.1 = Soft warning
channel-type.mercedesme.marker-rear-right.state.option.2 = Low warning
channel-type.mercedesme.marker-rear-right.state.option.3 = Deflation
channel-type.mercedesme.marker-rear-right.state.option.4 = Unknown warning
channel-type.mercedesme.max-soc.label = Charge SoC Maximum
channel-type.mercedesme.max-soc.command.option.50 % = 50 %
channel-type.mercedesme.max-soc.command.option.60 % = 60 %
channel-type.mercedesme.max-soc.command.option.70 % = 70 %
channel-type.mercedesme.max-soc.command.option.80 % = 80 %
channel-type.mercedesme.max-soc.command.option.90 % = 90 %
channel-type.mercedesme.max-soc.command.option.100 % = 100 %
channel-type.mercedesme.mileage.label = Mileage
channel-type.mercedesme.park-brake.label = Park Brake Active
channel-type.mercedesme.power.label = Charge Power
channel-type.mercedesme.pressure-front-left.label = Tire Pressure Front Left
channel-type.mercedesme.pressure-front-right.label = Tire Pressure Front Right
channel-type.mercedesme.pressure-rear-left.label = Tire Pressure Rear Left
channel-type.mercedesme.pressure-rear-right.label = Tire Pressure Rear Right
channel-type.mercedesme.program.label = Charge Program
channel-type.mercedesme.program.description = Selected Charge Program
channel-type.mercedesme.proto-update.label = Proto Data Update
channel-type.mercedesme.radius-electric.label = Electric Radius
channel-type.mercedesme.radius-fuel.label = Fuel Radius
channel-type.mercedesme.radius-hybrid.label = Hybrid Radius
channel-type.mercedesme.range-electric.label = Electric Range
channel-type.mercedesme.range-fuel.label = Fuel Range
channel-type.mercedesme.range-hybrid.label = Hybrid Range
channel-type.mercedesme.rear-blind.label = Window Blind Rear
channel-type.mercedesme.rear-left-blind.label = Window Blind Rear Left
channel-type.mercedesme.rear-left-hvac.label = AC Seat Rear Left
channel-type.mercedesme.rear-left-lock.label = Door Lock Rear Left
channel-type.mercedesme.rear-left-window.label = Window Rear Left
channel-type.mercedesme.rear-left-window.state.option.0 = Intermediate
channel-type.mercedesme.rear-left-window.state.option.1 = Open
channel-type.mercedesme.rear-left-window.state.option.2 = Closed
channel-type.mercedesme.rear-left-window.state.option.3 = Airing
channel-type.mercedesme.rear-left-window.state.option.4 = Intermediate
channel-type.mercedesme.rear-left-window.state.option.5 = Running
channel-type.mercedesme.rear-left.label = Door Rear Left
channel-type.mercedesme.rear-right-blind.label = Window Blind Rear Right
channel-type.mercedesme.rear-right-hvac.label = AC Seat Rear Right
channel-type.mercedesme.rear-right-lock.label = Door Lock Rear Right
channel-type.mercedesme.rear-right-window.label = Window Rear Right
channel-type.mercedesme.rear-right-window.state.option.0 = Intermediate
channel-type.mercedesme.rear-right-window.state.option.1 = Open
channel-type.mercedesme.rear-right-window.state.option.2 = Closed
channel-type.mercedesme.rear-right-window.state.option.3 = Airing
channel-type.mercedesme.rear-right-window.state.option.4 = Intermediate
channel-type.mercedesme.rear-right-window.state.option.5 = Running
channel-type.mercedesme.rear-right.label = Door Rear Right
channel-type.mercedesme.rooftop.label = Roof top
channel-type.mercedesme.rooftop.state.option.0 = Unlocked
channel-type.mercedesme.rooftop.state.option.1 = Open and locked
channel-type.mercedesme.rooftop.state.option.2 = Closed and locked
channel-type.mercedesme.sensor-available.label = Tire Sensor Available
channel-type.mercedesme.service-days.label = Service in Days
channel-type.mercedesme.signal.label = Position Signal
channel-type.mercedesme.signal.state.option.0 = Position Lights
channel-type.mercedesme.signal.state.option.1 = Position Horn
channel-type.mercedesme.signal.command.option.0 = Position Lights
channel-type.mercedesme.signal.command.option.1 = Position Horn
channel-type.mercedesme.soc.label = Battery Charge Level
channel-type.mercedesme.starter-battery.label = Starter Battery Status
channel-type.mercedesme.starter-battery.state.option.0 = Green
channel-type.mercedesme.starter-battery.state.option.1 = Yellow
channel-type.mercedesme.starter-battery.state.option.2 = Red
channel-type.mercedesme.sunroof-front-blind.label = Sun Roof Front Blind
channel-type.mercedesme.sunroof-rear-blind.label = Sun Roof Rear Blind
channel-type.mercedesme.sunroof.label = Sun Roof
channel-type.mercedesme.sunroof.state.option.0 = Closed
channel-type.mercedesme.sunroof.state.option.1 = Open
channel-type.mercedesme.sunroof.state.option.2 = Lifted
channel-type.mercedesme.sunroof.state.option.3 = Running
channel-type.mercedesme.sunroof.state.option.4 = Closing
channel-type.mercedesme.sunroof.state.option.5 = Opening
channel-type.mercedesme.sunroof.state.option.6 = Closing
channel-type.mercedesme.sunroof.command.option.0 = Close
channel-type.mercedesme.sunroof.command.option.1 = Open
channel-type.mercedesme.sunroof.command.option.2 = Lift
channel-type.mercedesme.temperature-hvac.label = AC Temperature
channel-type.mercedesme.tires-rdk.label = Tire Pressure Warnings
channel-type.mercedesme.uncharged.label = Uncharged Battery Energy
channel-type.mercedesme.wash-water.label = Wash Water Low
channel-type.mercedesme.windows.label = Windows Status/Control
channel-type.mercedesme.windows.state.option.0 = Intermediate
channel-type.mercedesme.windows.state.option.1 = Closed
channel-type.mercedesme.windows.state.option.2 = Open
channel-type.mercedesme.windows.command.option.0 = Ventilate
channel-type.mercedesme.windows.command.option.1 = Close
channel-type.mercedesme.windows.command.option.2 = Open
channel-type.mercedesme.zone-hvac.label = AC Zone
# MercedesMe Things Status Details
# channel types
actionPoiLabel = Send POI to Vehicle
actionPoiDescription = Send POI with name, latitude and longitude
poiTitle = POI Name
poiTitleDescription = Name of the location
latitudeLabel = Latitude
latitudeDescription = Latitude of the location
longitudeLabel = Longitude
longitudeDescription = Longitude of the location
mercedesme.account.status.authorization-needed = Manual Authorization needed at {0}
mercedesme.account.status.email-missing = EMail missing
mercedesme.account.status.region-missing = Region missing
mercedesme.account.status.refresh-invalid = Refresh Interval Invalid
mercedesme.account.status.ip-missing = Callback IP missing
mercedesme.account.status.port-missing = Callback Port missing
mercedesme.account.status.client-id-missing = Client ID missing
mercedesme.account.status.client-secret-missing = Client Secret missing
mercedesme.account.status.server-restart = Disable and enable Bridge to restart Authorization Server
mercedesme.vehicle.status.bridge-missing = Bridge not set
mercedesme.vehicle.status.bridge-authoriziation = Check Bridge Authorization
mercedesme.account.status.ip-autodetect-failure = Callback IP cannot be detected
mercedesme.account.status.websocket-failure = Websocket Exception: Reason: {0}

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="charge-flap">
<item-type>Number</item-type>
<label>Charge Flap</label>
<description>Charge Flap Status</description>
<state readOnly="true">
<options>
<option value="0">Open</option>
<option value="1">Closed</option>
</options>
</state>
</channel-type>
<channel-type id="coupler-ac">
<item-type>Number</item-type>
<label>Charge Coupler AC</label>
<description>Coupler AC Status</description>
<state readOnly="true">
<options>
<option value="0">Plugged</option>
<option value="2">Unplugged</option>
</options>
</state>
</channel-type>
<channel-type id="coupler-dc">
<item-type>Number</item-type>
<label>Charge Coupler DC</label>
<description>Coupler DC Status</description>
<state readOnly="true">
<options>
<option value="0">Plugged</option>
<option value="2">Unplugged</option>
</options>
</state>
</channel-type>
<channel-type id="coupler-lock">
<item-type>Number</item-type>
<label>Charge Coupler Lock</label>
<description>Coupler Lock Status</description>
<state readOnly="true">
<options>
<option value="0">Locked</option>
<option value="1">Unlocked</option>
</options>
</state>
</channel-type>
<channel-type id="active">
<item-type>Switch</item-type>
<label>Charge Active</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="power">
<item-type>Number:Power</item-type>
<label>Charge Power</label>
<state pattern="%.1f kW" readOnly="true"/>
</channel-type>
<channel-type id="program">
<item-type>Number</item-type>
<label>Charge Program</label>
<description>Selected Charge Program</description>
</channel-type>
<channel-type id="end-time">
<item-type>DateTime</item-type>
<label>Charge End Estimation</label>
<state pattern="%1$tH:%1$tM" readOnly="true"/>
</channel-type>
<channel-type id="max-soc">
<item-type>Number:Dimensionless</item-type>
<label>Charge SoC Maximum</label>
<state pattern="%d %%"/>
<command>
<options>
<option value="50 %">50 %</option>
<option value="60 %">60 %</option>
<option value="70 %">70 %</option>
<option value="80 %">80 %</option>
<option value="90 %">90 %</option>
<option value="100 %">100 %</option>
</options>
</command>
</channel-type>
<channel-type id="auto-unlock">
<item-type>Switch</item-type>
<label>Coupler Auto Unlock</label>
<description>Auto unlock coupler after charging </description>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="charge-values">
<label>Charging Data</label>
<channels>
<channel id="charge-flap" typeId="charge-flap"/>
<channel id="coupler-ac" typeId="coupler-ac"/>
<channel id="coupler-dc" typeId="coupler-dc"/>
<channel id="coupler-lock" typeId="coupler-lock"/>
<channel id="active" typeId="active"/>
<channel id="power" typeId="power"/>
<channel id="end-time" typeId="end-time"/>
<channel id="program" typeId="program"/>
<channel id="max-soc" typeId="max-soc"/>
<channel id="auto-unlock" typeId="auto-unlock"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -3,14 +3,19 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="heading-channel">
<item-type>Number:Angle</item-type>
<label>Heading Angle</label>
<state pattern="%d %unit%" readOnly="true"/>
<channel-type id="cmd-name">
<item-type>Number</item-type>
<label>Command Name</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="last-location-update-channel">
<channel-type id="cmd-state">
<item-type>Number</item-type>
<label>Command State</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="cmd-last-update">
<item-type>DateTime</item-type>
<label>Last Location Update</label>
<label>Command Updated</label>
<state pattern="%1$tA, %1$td.%1$tm. %1$tH:%1$tM" readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -3,12 +3,12 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="image-values">
<label>Vehicle Images</label>
<channel-group-type id="command-values">
<label>Status of Last Command</label>
<channels>
<channel id="image-data" typeId="image-data-channel"/>
<channel id="image-view" typeId="image-view-channel"/>
<channel id="clear-cache" typeId="clear-cache-channel"/>
<channel id="cmd-name" typeId="cmd-name"/>
<channel id="cmd-state" typeId="cmd-state"/>
<channel id="cmd-last-update" typeId="cmd-last-update"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -3,32 +3,37 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="driver-front-channel">
<channel-type id="front-left">
<item-type>Contact</item-type>
<label>Driver Door</label>
<label>Door Front Left</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="driver-rear-channel">
<channel-type id="front-right">
<item-type>Contact</item-type>
<label>Driver Door Rear</label>
<label>Door Front Right</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="passenger-front-channel">
<channel-type id="rear-left">
<item-type>Contact</item-type>
<label>Passenger Door</label>
<label>Door Rear Left</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="passenger-rear-channel">
<channel-type id="rear-right">
<item-type>Contact</item-type>
<label>Passenger Door Rear</label>
<label>Door Rear Right</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="deck-lid-channel">
<channel-type id="deck-lid">
<item-type>Contact</item-type>
<label>Deck Lid</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="rooftop-channel">
<channel-type id="engine-hood">
<item-type>Contact</item-type>
<label>Engine Hood</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="rooftop">
<item-type>Number</item-type>
<label>Roof top</label>
<state readOnly="true">
@ -39,24 +44,37 @@
</options>
</state>
</channel-type>
<channel-type id="sunroof-channel">
<channel-type id="sunroof">
<item-type>Number</item-type>
<label>Sun Roof</label>
<state readOnly="true">
<state>
<options>
<option value="0">Closed</option>
<option value="1">Open</option>
<option value="2">Open Lifting</option>
<option value="2">Lifted</option>
<option value="3">Running</option>
<option value="4">Closing</option>
<option value="5">Opening</option>
<option value="6">Closing</option>
</options>
</state>
<command>
<options>
<option value="0">Close</option>
<option value="1">Open</option>
<option value="2">Lift</option>
</options>
</command>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="last-doors-update-channel">
<item-type>DateTime</item-type>
<label>Last Doors Update</label>
<state pattern="%1$tA, %1$td.%1$tm. %1$tH:%1$tM" readOnly="true"/>
<channel-type id="sunroof-front-blind">
<item-type>Number</item-type>
<label>Sun Roof Front Blind</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="sunroof-rear-blind">
<item-type>Number</item-type>
<label>Sun Roof Rear Blind</label>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -7,14 +7,16 @@
<label>Detailed Door Status</label>
<description>Detailed Status of all Doors and Windows</description>
<channels>
<channel id="driver-front" typeId="driver-front-channel"/>
<channel id="driver-rear" typeId="driver-rear-channel"/>
<channel id="passenger-front" typeId="passenger-front-channel"/>
<channel id="passenger-rear" typeId="passenger-rear-channel"/>
<channel id="deck-lid" typeId="deck-lid-channel"/>
<channel id="sunroof" typeId="sunroof-channel"/>
<channel id="rooftop" typeId="rooftop-channel"/>
<channel id="last-update" typeId="last-doors-update-channel"/>
<channel id="front-left" typeId="front-left"/>
<channel id="front-right" typeId="front-right"/>
<channel id="rear-left" typeId="rear-left"/>
<channel id="rear-right" typeId="rear-right"/>
<channel id="deck-lid" typeId="deck-lid"/>
<channel id="engine-hood" typeId="engine-hood"/>
<channel id="rooftop" typeId="rooftop"/>
<channel id="sunroof" typeId="sunroof"/>
<channel id="sunroof-front-blind" typeId="sunroof-front-blind"/>
<channel id="sunroof-rear-blind" typeId="sunroof-rear-blind"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="zone-hvac">
<item-type>Number</item-type>
<label>AC Zone</label>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="temperature-hvac">
<item-type>Number:Temperature</item-type>
<label>AC Temperature</label>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="active-hvac">
<item-type>Switch</item-type>
<label>AC Control</label>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="front-left-hvac">
<item-type>Switch</item-type>
<label>AC Seat Front Left</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="front-right-hvac">
<item-type>Switch</item-type>
<label>AC Seat Front Right</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="rear-left-hvac">
<item-type>Switch</item-type>
<label>AC Seat Rear Left</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="rear-right-hvac">
<item-type>Switch</item-type>
<label>AC Seat Rear Right</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="aux-heat">
<item-type>Switch</item-type>
<label>Auxiliary Heating Control</label>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="hvac-values">
<label>Vehicle Climate Control</label>
<channels>
<channel id="front-left" typeId="front-left-hvac"/>
<channel id="front-right" typeId="front-right-hvac"/>
<channel id="rear-left" typeId="rear-left-hvac"/>
<channel id="rear-right" typeId="rear-right-hvac"/>
<channel id="zone" typeId="zone-hvac"/>
<channel id="temperature" typeId="temperature-hvac"/>
<channel id="active" typeId="active-hvac"/>
<channel id="aux-heat" typeId="aux-heat"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="image-data-channel">
<item-type>Image</item-type>
<label>Rendered Vehicle Image</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="image-view-channel">
<item-type>String</item-type>
<label>Image Viewport</label>
</channel-type>
<channel-type id="clear-cache-channel">
<item-type>Switch</item-type>
<label>Remove All Stored Images</label>
</channel-type>
</thing:thing-descriptions>

View File

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="interior-front-channel">
<item-type>Switch</item-type>
<label>Interior Light Front</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="interior-rear-channel">
<item-type>Switch</item-type>
<label>Interior Light Rear</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="reading-left-channel">
<item-type>Switch</item-type>
<label>Reading Light Left</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="reading-right-channel">
<item-type>Switch</item-type>
<label>Reading Light Right</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="light-switch-channel">
<item-type>Number</item-type>
<label>Main Light Rotary</label>
<state readOnly="true">
<options>
<option value="0">Auto</option>
<option value="1">Headlight</option>
<option value="2">Sidelight Left</option>
<option value="3">Sidelight Right</option>
<option value="4">Parking Light</option>
</options>
</state>
</channel-type>
<channel-type id="last-lights-update-channel">
<item-type>DateTime</item-type>
<label>Last Light Update</label>
<state pattern="%1$tA, %1$td.%1$tm. %1$tH:%1$tM" readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="light-values">
<label>Light Status</label>
<description>Light Status of interior lights and main light switch</description>
<channels>
<channel id="interior-front" typeId="interior-front-channel"/>
<channel id="interior-rear" typeId="interior-rear-channel"/>
<channel id="light-switch" typeId="light-switch-channel"/>
<channel id="reading-left" typeId="reading-left-channel"/>
<channel id="reading-right" typeId="reading-right-channel"/>
<channel id="last-update" typeId="last-lights-update-channel"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -3,31 +3,34 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="doors-lock-channel">
<item-type>Number</item-type>
<label>Door Lock Status</label>
<state readOnly="true">
<options>
<option value="0">Unlocked</option>
<option value="1">Locked Internal</option>
<option value="2">Locked External</option>
<option value="3">Unlocked Selective</option>
</options>
</state>
<channel-type id="front-left-lock">
<item-type>Switch</item-type>
<label>Door Lock Front Left</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="deck-lid-lock-channel">
<channel-type id="front-right-lock">
<item-type>Switch</item-type>
<label>Door Lock Front Right</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="rear-left-lock">
<item-type>Switch</item-type>
<label>Door Lock Rear Left</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="rear-right-lock">
<item-type>Switch</item-type>
<label>Door Lock Rear Right</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="deck-lid-lock">
<item-type>Switch</item-type>
<label>Deck Lid Lock</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="flap-lock-channel">
<channel-type id="gas-flap-lock">
<item-type>Switch</item-type>
<label>Flap Lock</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="last-lock-update-channel">
<item-type>DateTime</item-type>
<label>Last Lock Update</label>
<state pattern="%1$tA, %1$td.%1$tm. %1$tH:%1$tM" readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="lock-ev-values">
<label>Lock Status</label>
<description>Door Lock Status</description>
<channels>
<channel id="front-left" typeId="front-left-lock"/>
<channel id="front-right" typeId="front-right-lock"/>
<channel id="rear-left" typeId="rear-left-lock"/>
<channel id="rear-right" typeId="rear-right-lock"/>
<channel id="deck-lid" typeId="deck-lid-lock"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -5,12 +5,14 @@
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="lock-values">
<label>Lock Status</label>
<description>Vehicle Lock Status</description>
<description>Door Lock Status</description>
<channels>
<channel id="doors" typeId="doors-lock-channel"/>
<channel id="deck-lid" typeId="deck-lid-lock-channel"/>
<channel id="flap" typeId="flap-lock-channel"/>
<channel id="last-update" typeId="last-lock-update-channel"/>
<channel id="front-left" typeId="front-left-lock"/>
<channel id="front-right" typeId="front-right-lock"/>
<channel id="rear-left" typeId="rear-left-lock"/>
<channel id="rear-right" typeId="rear-right-lock"/>
<channel id="deck-lid" typeId="deck-lid-lock"/>
<channel id="gas-flap" typeId="gas-flap-lock"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -3,12 +3,13 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="location-values">
<channel-group-type id="position-values">
<label>Vehicle Location</label>
<description>Heading of vehicle</description>
<channels>
<channel id="heading" typeId="heading-channel"/>
<channel id="last-update" typeId="last-location-update-channel"/>
<channel id="heading" typeId="heading"/>
<channel id="gps" typeId="gps"/>
<channel id="signal" typeId="signal"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="heading">
<item-type>Number:Angle</item-type>
<label>Position Heading Angle</label>
<state pattern="%d %unit%" readOnly="true"/>
</channel-type>
<channel-type id="gps">
<item-type>Location</item-type>
<label>Position GPS</label>
</channel-type>
<channel-type id="signal">
<item-type>Number</item-type>
<label>Position Signal</label>
<state>
<options>
<option value="0">Position Lights</option>
<option value="1">Position Horn</option>
</options>
</state>
<command>
<options>
<option value="0">Position Lights</option>
<option value="1">Position Horn</option>
</options>
</command>
</channel-type>
</thing:thing-descriptions>

View File

@ -3,74 +3,74 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="mileage-channel">
<channel-type id="mileage">
<item-type>Number:Length</item-type>
<label>Mileage</label>
<state pattern="%d %unit%" readOnly="true"/>
<state pattern="%d km" readOnly="true"/>
</channel-type>
<channel-type id="range-electric-channel">
<channel-type id="range-electric">
<item-type>Number:Length</item-type>
<label>Electric Range</label>
<state pattern="%d %unit%" readOnly="true"/>
<state pattern="%d km" readOnly="true"/>
</channel-type>
<channel-type id="radius-electric-channel">
<channel-type id="radius-electric">
<item-type>Number:Length</item-type>
<label>Electric Radius</label>
<state pattern="%d %unit%" readOnly="true"/>
<state pattern="%d km" readOnly="true"/>
</channel-type>
<channel-type id="soc-channel">
<channel-type id="home-distance">
<item-type>Number:Length</item-type>
<label>Distance to Home</label>
<state pattern="%.3f km" readOnly="true"/>
</channel-type>
<channel-type id="soc">
<item-type>Number:Dimensionless</item-type>
<label>Battery Charge Level</label>
<state pattern="%d %unit%" readOnly="true"/>
<state pattern="%d %" readOnly="true"/>
</channel-type>
<channel-type id="charged-channel">
<channel-type id="charged">
<item-type>Number:Energy</item-type>
<label>Charged Battery Energy</label>
<state pattern="%.2f %unit%" readOnly="true"/>
<state pattern="%.2f kWh" readOnly="true"/>
</channel-type>
<channel-type id="uncharged-channel">
<channel-type id="uncharged">
<item-type>Number:Energy</item-type>
<label>Uncharged Battery Energy</label>
<state pattern="%.2f %unit%" readOnly="true"/>
<state pattern="%.2f kWh" readOnly="true"/>
</channel-type>
<channel-type id="range-fuel-channel">
<channel-type id="range-fuel">
<item-type>Number:Length</item-type>
<label>Fuel Range</label>
<state pattern="%d %unit%" readOnly="true"/>
<state pattern="%d km" readOnly="true"/>
</channel-type>
<channel-type id="radius-fuel-channel">
<channel-type id="radius-fuel">
<item-type>Number:Length</item-type>
<label>Fuel Radius</label>
<state pattern="%d %unit%" readOnly="true"/>
<state pattern="%d km" readOnly="true"/>
</channel-type>
<channel-type id="fuel-level-channel">
<channel-type id="fuel-level">
<item-type>Number:Dimensionless</item-type>
<label>Fuel Level</label>
<state pattern="%d %unit%" readOnly="true"/>
<state pattern="%d %%" readOnly="true"/>
</channel-type>
<channel-type id="fuel-remain-channel">
<channel-type id="fuel-remain">
<item-type>Number:Volume</item-type>
<label>Remaining Fuel</label>
<state pattern="%.2f %unit%" readOnly="true"/>
<state pattern="%.2f l" readOnly="true"/>
</channel-type>
<channel-type id="fuel-open-channel">
<channel-type id="fuel-open">
<item-type>Number:Volume</item-type>
<label>Open Fuel Capacity</label>
<state pattern="%.2f %unit%" readOnly="true"/>
<state pattern="%.2f l" readOnly="true"/>
</channel-type>
<channel-type id="range-hybrid-channel">
<channel-type id="range-hybrid">
<item-type>Number:Length</item-type>
<label>Hybrid Range</label>
<state pattern="%d %unit%" readOnly="true"/>
<state pattern="%d km" readOnly="true"/>
</channel-type>
<channel-type id="radius-hybrid-channel">
<channel-type id="radius-hybrid">
<item-type>Number:Length</item-type>
<label>Hybrid Radius</label>
<state pattern="%d %unit%" readOnly="true"/>
</channel-type>
<channel-type id="last-range-update-channel">
<item-type>DateTime</item-type>
<label>Last Range Update</label>
<state pattern="%1$tA, %1$td.%1$tm. %1$tH:%1$tM" readOnly="true"/>
<state pattern="%d km" readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -7,13 +7,13 @@
<label>Range and Fuel Data</label>
<description>Provides Mileage, remaining range and fuel level values</description>
<channels>
<channel id="mileage" typeId="mileage-channel"/>
<channel id="range-fuel" typeId="range-fuel-channel"/>
<channel id="radius-fuel" typeId="radius-fuel-channel"/>
<channel id="fuel-level" typeId="fuel-level-channel"/>
<channel id="fuel-remain" typeId="fuel-remain-channel"/>
<channel id="fuel-open" typeId="fuel-open-channel"/>
<channel id="last-update" typeId="last-range-update-channel"/>
<channel id="mileage" typeId="mileage"/>
<channel id="range-fuel" typeId="range-fuel"/>
<channel id="radius-fuel" typeId="radius-fuel"/>
<channel id="home-distance" typeId="home-distance"/>
<channel id="fuel-level" typeId="fuel-level"/>
<channel id="fuel-remain" typeId="fuel-remain"/>
<channel id="fuel-open" typeId="fuel-open"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -4,16 +4,16 @@
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="range-ev-values">
<label>Range and Charge Data</label>
<label>Range and Battery Data</label>
<description>Provides Mileage, remaining range and charge level values</description>
<channels>
<channel id="mileage" typeId="mileage-channel"/>
<channel id="range-electric" typeId="range-electric-channel"/>
<channel id="radius-electric" typeId="radius-electric-channel"/>
<channel id="soc" typeId="soc-channel"/>
<channel id="charged" typeId="charged-channel"/>
<channel id="uncharged" typeId="uncharged-channel"/>
<channel id="last-update" typeId="last-range-update-channel"/>
<channel id="mileage" typeId="mileage"/>
<channel id="range-electric" typeId="range-electric"/>
<channel id="radius-electric" typeId="radius-electric"/>
<channel id="home-distance" typeId="home-distance"/>
<channel id="soc" typeId="soc"/>
<channel id="charged" typeId="charged"/>
<channel id="uncharged" typeId="uncharged"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -4,23 +4,23 @@
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="range-hybrid-values">
<label>Range, Charge / Fuel Data</label>
<label>Range, Battery and Fuel Data</label>
<description>Provides mileage, remaining fuel and range data for hybrid vehicles</description>
<channels>
<channel id="mileage" typeId="mileage-channel"/>
<channel id="range-electric" typeId="range-electric-channel"/>
<channel id="radius-electric" typeId="radius-electric-channel"/>
<channel id="soc" typeId="soc-channel"/>
<channel id="charged" typeId="charged-channel"/>
<channel id="uncharged" typeId="uncharged-channel"/>
<channel id="range-fuel" typeId="range-fuel-channel"/>
<channel id="radius-fuel" typeId="radius-fuel-channel"/>
<channel id="fuel-level" typeId="fuel-level-channel"/>
<channel id="fuel-remain" typeId="fuel-remain-channel"/>
<channel id="fuel-open" typeId="fuel-open-channel"/>
<channel id="range-hybrid" typeId="range-hybrid-channel"/>
<channel id="radius-hybrid" typeId="radius-fuel-channel"/>
<channel id="last-update" typeId="last-range-update-channel"/>
<channel id="mileage" typeId="mileage"/>
<channel id="range-electric" typeId="range-electric"/>
<channel id="radius-electric" typeId="radius-electric"/>
<channel id="home-distance" typeId="home-distance"/>
<channel id="soc" typeId="soc"/>
<channel id="charged" typeId="charged"/>
<channel id="uncharged" typeId="uncharged"/>
<channel id="range-fuel" typeId="range-fuel"/>
<channel id="radius-fuel" typeId="radius-fuel"/>
<channel id="fuel-level" typeId="fuel-level"/>
<channel id="fuel-remain" typeId="fuel-remain"/>
<channel id="fuel-open" typeId="fuel-open"/>
<channel id="range-hybrid" typeId="range-hybrid"/>
<channel id="radius-hybrid" typeId="radius-fuel"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="starter-battery">
<item-type>Number</item-type>
<label>Starter Battery Status</label>
<state readOnly="true">
<options>
<option value="0">Green</option>
<option value="1">Yellow</option>
<option value="2">Red</option>
</options>
</state>
</channel-type>
<channel-type id="brake-fluid">
<item-type>Switch</item-type>
<label>Brake Fluid</label>
<state readOnly="true"></state>
</channel-type>
<channel-type id="brake-lining-wear">
<item-type>Switch</item-type>
<label>Brake Lining Wear</label>
<state readOnly="true"></state>
</channel-type>
<channel-type id="wash-water">
<item-type>Switch</item-type>
<label>Wash Water Low</label>
<state readOnly="true"></state>
</channel-type>
<channel-type id="coolant-fluid">
<item-type>Switch</item-type>
<label>Coolant Fluid Low</label>
<state readOnly="true"></state>
</channel-type>
<channel-type id="engine">
<item-type>Switch</item-type>
<label>Engine Warning</label>
<state readOnly="true"></state>
</channel-type>
<channel-type id="tires-rdk">
<item-type>Number</item-type>
<label>Tire Pressure Warnings</label>
<state readOnly="true">
</state>
</channel-type>
<channel-type id="service-days">
<item-type>Number</item-type>
<label>Service in Days</label>
<state readOnly="true">
</state>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="service-ev-values">
<label>Service and Warnings</label>
<channels>
<channel id="starter-battery" typeId="starter-battery"/>
<channel id="brake-fluid" typeId="brake-fluid"/>
<channel id="brake-lining-wear" typeId="brake-lining-wear"/>
<channel id="wash-water" typeId="wash-water"/>
<channel id="tires-rdk" typeId="tires-rdk"/>
<channel id="service-days" typeId="service-days"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="service-values">
<label>Warnings</label>
<description>Current active warnings</description>
<channels>
<channel id="starter-battery" typeId="starter-battery"/>
<channel id="brake-fluid" typeId="brake-fluid"/>
<channel id="brake-lining-wear" typeId="brake-lining-wear"/>
<channel id="wash-water" typeId="wash-water"/>
<channel id="tires-rdk" typeId="tires-rdk"/>
<channel id="coolant-fluid" typeId="coolant-fluid"/>
<channel id="engine" typeId="engine"/>
<channel id="service-days" typeId="service-days"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -13,13 +13,18 @@
<description>Battery Electric Vehicle</description>
<channel-groups>
<channel-group id="range" typeId="range-ev-values"/>
<channel-group id="vehicle" typeId="vehicle-values"/>
<channel-group id="doors" typeId="door-values"/>
<channel-group id="lock" typeId="lock-ev-values"/>
<channel-group id="windows" typeId="window-values"/>
<channel-group id="lights" typeId="light-values"/>
<channel-group id="lock" typeId="lock-values"/>
<channel-group id="location" typeId="location-values"/>
<channel-group id="image" typeId="image-values"/>
<channel-group id="hvac" typeId="hvac-values"/>
<channel-group id="service" typeId="service-ev-values"/>
<channel-group id="range" typeId="range-ev-values"/>
<channel-group id="charge" typeId="charge-values"/>
<channel-group id="trip" typeId="trip-ev-values"/>
<channel-group id="position" typeId="position-values"/>
<channel-group id="tires" typeId="tires-values"/>
<channel-group id="command" typeId="command-values"/>
</channel-groups>
<config-description-ref uri="thing-type:mercedesme:bev"/>

View File

@ -13,13 +13,17 @@
<description>Conventional Fuel Vehicle</description>
<channel-groups>
<channel-group id="range" typeId="range-conv-values"/>
<channel-group id="vehicle" typeId="vehicle-values"/>
<channel-group id="doors" typeId="door-values"/>
<channel-group id="windows" typeId="window-values"/>
<channel-group id="lights" typeId="light-values"/>
<channel-group id="lock" typeId="lock-values"/>
<channel-group id="location" typeId="location-values"/>
<channel-group id="image" typeId="image-values"/>
<channel-group id="windows" typeId="window-values"/>
<channel-group id="hvac" typeId="hvac-values"/>
<channel-group id="service" typeId="service-values"/>
<channel-group id="range" typeId="range-conv-values"/>
<channel-group id="trip" typeId="trip-values"/>
<channel-group id="position" typeId="position-values"/>
<channel-group id="tires" typeId="tires-values"/>
<channel-group id="command" typeId="command-values"/>
</channel-groups>
<config-description-ref uri="thing-type:mercedesme:conv"/>

View File

@ -13,13 +13,18 @@
<description>Conventional Fuel Vehicle with supporting Electric Engine</description>
<channel-groups>
<channel-group id="range" typeId="range-hybrid-values"/>
<channel-group id="vehicle" typeId="vehicle-values"/>
<channel-group id="doors" typeId="door-values"/>
<channel-group id="windows" typeId="window-values"/>
<channel-group id="lights" typeId="light-values"/>
<channel-group id="lock" typeId="lock-values"/>
<channel-group id="location" typeId="location-values"/>
<channel-group id="image" typeId="image-values"/>
<channel-group id="windows" typeId="window-values"/>
<channel-group id="hvac" typeId="hvac-values"/>
<channel-group id="service" typeId="service-values"/>
<channel-group id="range" typeId="range-hybrid-values"/>
<channel-group id="charge" typeId="charge-values"/>
<channel-group id="trip" typeId="trip-hybrid-values"/>
<channel-group id="position" typeId="position-values"/>
<channel-group id="tires" typeId="tires-values"/>
<channel-group id="command" typeId="command-values"/>
</channel-groups>
<config-description-ref uri="thing-type:mercedesme:hybrid"/>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="pressure-front-right">
<item-type>Number:Pressure</item-type>
<label>Tire Pressure Front Right</label>
<state pattern="%.1f bar" readOnly="true"/>
</channel-type>
<channel-type id="pressure-rear-right">
<item-type>Number:Pressure</item-type>
<label>Tire Pressure Rear Right</label>
<state pattern="%.1f bar" readOnly="true"/>
</channel-type>
<channel-type id="pressure-front-left">
<item-type>Number:Pressure</item-type>
<label>Tire Pressure Front Left</label>
<state pattern="%.1f bar" readOnly="true"/>
</channel-type>
<channel-type id="pressure-rear-left">
<item-type>Number:Pressure</item-type>
<label>Tire Pressure Rear Left</label>
<state pattern="%.1f bar" readOnly="true"/>
</channel-type>
<channel-type id="sensor-available">
<item-type>Number</item-type>
<label>Tire Sensor Available</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="marker-front-right">
<item-type>Number</item-type>
<label>Tire Warning Front Right</label>
<state readOnly="true">
<options>
<option value="0">No warning</option>
<option value="1">Soft warning</option>
<option value="2">Low warning</option>
<option value="3">Deflation</option>
<option value="4">Unknown warning</option>
</options>
</state>
</channel-type>
<channel-type id="marker-rear-right">
<item-type>Number</item-type>
<label>Tire Warning Rear Right</label>
<state readOnly="true">
<options>
<option value="0">No warning</option>
<option value="1">Soft warning</option>
<option value="2">Low warning</option>
<option value="3">Deflation</option>
<option value="4">Unknown warning</option>
</options>
</state>
</channel-type>
<channel-type id="marker-front-left">
<item-type>Number</item-type>
<label>Tire Warning Front Left</label>
<state readOnly="true">
<options>
<option value="0">No warning</option>
<option value="1">Soft warning</option>
<option value="2">Low warning</option>
<option value="3">Deflation</option>
<option value="4">Unknown warning</option>
</options>
</state>
</channel-type>
<channel-type id="marker-rear-left">
<item-type>Number</item-type>
<label>Tire Warning Rear Left</label>
<state readOnly="true">
<options>
<option value="0">No warning</option>
<option value="1">Soft warning</option>
<option value="2">Low warning</option>
<option value="3">Deflation</option>
<option value="4">Unknown warning</option>
</options>
</state>
</channel-type>
<channel-type id="last-update">
<item-type>DateTime</item-type>
<label>Tire Update Time</label>
<state pattern="%1$tA, %1$td.%1$tm. %1$tH:%1$tM" readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-group-type id="tires-values">
<label>Tire Status</label>
<description>Tire Pressure Values</description>
<channels>
<channel id="pressure-front-right" typeId="pressure-front-right"/>
<channel id="pressure-rear-right" typeId="pressure-rear-right"/>
<channel id="pressure-front-left" typeId="pressure-front-left"/>
<channel id="pressure-rear-left" typeId="pressure-rear-left"/>
<channel id="sensor-available" typeId="sensor-available"/>
<channel id="marker-front-right" typeId="marker-front-right"/>
<channel id="marker-rear-right" typeId="marker-rear-right"/>
<channel id="marker-front-left" typeId="marker-front-left"/>
<channel id="marker-rear-left" typeId="marker-rear-left"/>
<channel id="last-update" typeId="last-update"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mercedesme"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="distance">
<item-type>Number:Length</item-type>
<label>Trip Distance</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="driven-time">
<item-type>String</item-type>
<label>Trip String Time</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="avg-speed">
<item-type>Number:Speed</item-type>
<label>Trip Average Speed</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="consumption-ev">
<item-type>Number</item-type>
<label>Trip Avg Cons EV</label>
<description>Last Trip Average Consumption Electric</description>
<state pattern="%.1f" readOnly="true"/>
</channel-type>
<channel-type id="consumption-conv">
<item-type>Number</item-type>
<label>Trip Avg Cons Fuel</label>
<description>Last Trip Average Consumption Fuel</description>
<state pattern="%.1f" readOnly="true"/>
</channel-type>
<channel-type id="distance-reset">
<item-type>Number:Length</item-type>
<label>Rst Distance</label>
<description>Distance since last Reset</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="driven-time-reset">
<item-type>String</item-type>
<label>Rst Driving Time</label>
<description>Driving time since last Reset</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="avg-speed-reset">
<item-type>Number:Speed</item-type>
<label>Rst Average Speed</label>
<description>Average speed since last Reset</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="consumption-ev-reset">
<item-type>Number</item-type>
<label>Avg Cons Rst EV</label>
<description>Average consumption since last reset electric</description>
<state pattern="%.1f" readOnly="true"/>
</channel-type>
<channel-type id="consumption-conv-reset">
<item-type>Number</item-type>
<label>Avg Cons Rst Fuel</label>
<description>Average consumption since last reset fuel</description>
<state pattern="%.1f" readOnly="true"/>
</channel-type>
<channel-type id="consumption-ev-unit">
<item-type>String</item-type>
<label>Avg Cons Unit EV</label>
<description>Average consumption unit electric</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="consumption-conv-unit">
<item-type>String</item-type>
<label>Avg Cons Unit Fuel</label>
<description>Average consumption unit fuel</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

Some files were not shown because too many files have changed in this diff Show More