/bundles/org.openhab.binding.bluetooth.govee/ @cpmeister
/bundles/org.openhab.binding.bluetooth.roaming/ @cpmeister
/bundles/org.openhab.binding.bluetooth.ruuvitag/ @ssalonen
-/bundles/org.openhab.binding.bmwconnecteddrive/ @weymann @ntruchsess
/bundles/org.openhab.binding.boschindego/ @jofleck
/bundles/org.openhab.binding.boschshc/ @stefan-kaestle @coeing @GerdZanker
/bundles/org.openhab.binding.bosesoundtouch/ @marvkis @tratho
<artifactId>org.openhab.binding.bluetooth.ruuvitag</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
- <groupId>org.openhab.addons.bundles</groupId>
- <artifactId>org.openhab.binding.bmwconnecteddrive</artifactId>
- <version>${project.version}</version>
- </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.boschindego</artifactId>
+++ /dev/null
-This content is produced and maintained by the openHAB project.
-
-* Project home: https://www.openhab.org
-
-== Declared Project Licenses
-
-This program and the accompanying materials are made available under the terms
-of the Eclipse Public License 2.0 which is available at
-https://www.eclipse.org/legal/epl-2.0/.
-
-== Source Code
-
-https://github.com/openhab/openhab-addons
+++ /dev/null
-# BMW ConnectedDrive Binding
-
-The binding provides a connection between [BMW's ConnectedDrive Portal](https://www.bmw-connecteddrive.com/country-region-select/country-region-selection.html) and openHAB.
-All vehicles connected to an account will be detected by the discovery with the correct type
-
-* Conventional Fuel Vehicle
-* Plugin-Hybrid Electrical Vehicle
-* Battery Electric Vehicle with Range Extender
-* Battery Electric Vehicle
-
-In addition properties are attached with information and services provided by this vehicle.
-The provided data depends on
-
-1. the [Thing Type](#things) and
-2. the [Properties](#properties) mentioned in Services
-
-Different channel groups are clustering all informations.
-Check for each group if it's supported for this Vehicle.
-
-Please note **this isn't a real-time binding**.
-If a door is opened the state isn't transmitted and changed immediately.
-This isn't a flaw in the binding itself because the state in BMW's own ConnectedDrive App is also updated with some delay.
-
-## Supported Things
-
-### Bridge
-
-The bridge establishes the connection between BMW's ConnectedDrive Portal and openHAB.
-
-| Name | Bridge Type ID | Description |
-|----------------------------|----------------|------------------------------------------------------------|
-| BMW ConnectedDrive Account | `account` | Access to BMW ConnectedDrive Portal for a specific user |
-
-
-### Things
-
-Four different vehicle types are provided.
-They differ in the supported channel groups & channels.
-Conventional Fuel Vehicles have no _Charging Profile_, Electric Vehicles don't provide a _Fuel Range_.
-For hybrid vehicles in addition to _Fuel and Electric Range_ the _Hybrid Range_ is shown.
-
-| Name | Thing Type ID | Supported Channel Groups |
-|-------------------------------------|---------------|--------------------------------------------------------|
-| BMW Electric Vehicle | `bev` | status, range, location, service, check, charge, image |
-| BMW Electric Vehicle with REX | `bev_rex` | status, range, location, service, check, charge, image |
-| BMW Plug-In-Hybrid Electric Vehicle | `phev` | status, range, location, service, check, charge, image |
-| BMW Conventional Vehicle | `conv` | status, range, location, service, check, image |
-
-
-#### Properties
-
-<img align="right" src="./doc/properties.png" width="500" height="225"/>
-
-For each vehicle properties are available.
-Basically 3 types of information are registered as properties
-
-* Informations regarding your dealer with address and phone number
-* Which services are available / not available
-* Vehicle properties like color, model type, drive train and construction year
-
-In the right picture can see in *Services Activated* e.g. the *DoorLock* and *DoorUnlock* services are mentioned.
-This ensures channel group [Remote Services](#remote-services) is supporting door lock and unlock remote control.
-
-In *Services Supported* the entry *LastDestination* is mentioned.
-So it's valid to connect channel group [Last Destinations](#destinations) in order to display and select the last navigation destinations.
-
-| Property Key | Property Value | Supported Channel Groups |
-|--------------------|---------------------|------------------------------|
-| servicesSupported | Statistics | last-trip, lifetime |
-| servicesSupported | LastDestinations | destinations |
-| servicesActivated | _list of services_ | remote |
-
-
-## Discovery
-
-Auto discovery is starting after the bridge towards BMW's ConnectedDrive is created.
-A list of your registered vehicles is queried and all found things are added in the inbox.
-Unique identifier is the *Vehicle Identification Number* (VIN).
-If a thing is already declared in a _.things_ configuration, discovery won't highlight it again.
-Properties will be attached to predefined vehicles if the VIN is matching.
-
-## Configuration
-
-### Bridge Configuration
-
-| Parameter | Type | Description |
-|-----------------|---------|--------------------------------------------------------------------|
-| userName | text | BMW ConnectedDrive Username |
-| password | text | BMW ConnectedDrive Password |
-| region | text | Select region in order to connect to the appropriate BMW server. |
-
-The region Configuration has 3 different options
-
-* _NORTH_AMERICA_
-* _CHINA_
-* _ROW_ (Rest of World)
-
-
-#### Advanced Configuration
-
-| Parameter | Type | Description |
-|-----------------|---------|--------------------------------------------------------------------|
-| preferMyBmw | boolean | Prefer *MyBMW* API instead of *BMW Connected Drive* |
-
-
-### Thing Configuration
-
-Same configuration is needed for all things
-
-| Parameter | Type | Description |
-|-----------------|---------|---------------------------------------|
-| vin | text | Vehicle Identification Number (VIN) |
-| refreshInterval | integer | Refresh Interval in Minutes |
-| units | text | Unit Selection. See below. |
-| imageSize | integer | Image Size |
-| imageViewport | text | Image Viewport |
-
-The unit configuration has 3 options
-
-* _AUTODETECT_ selects miles for US & UK, kilometer otherwise
-* _METRIC_ selects directly kilometers
-* _IMPERIAL_ selects directly miles
-
-The _imageVieport_ allows to show the vehicle from different angels.
-Possible options are
-
-* _FRONT_
-* _REAR_
-* _SIDE_
-* _DASHBOARD_
-* _DRIVERDOOR_
-
-## Channels
-
-There are many channels available for each vehicle.
-For better overview they are clustered in different channel groups.
-They differ for each vehicle type, build-in sensors and activated services.
-
-
-### Thing Channel Groups
-
-#### Vehicle Status
-
-Reflects overall status of the vehicle.
-
-* Channel Group ID is **status**
-* Available for all vehicles
-* Read-only values
-
-| Channel Label | Channel ID | Type | Description |
-|---------------------------|---------------------|---------------|------------------------------------------------|
-| Overall Door Status | doors | String | Combined status for all doors |
-| Overall Window Status | windows | String | Combined status for all windows |
-| Doors Locked | lock | String | Status if doors are locked or unlocked |
-| Next Service Date | service-date | DateTime | Date of upcoming service |
-| Mileage till Next Service | service-mileage | Number:Length | Mileage till upcoming service |
-| Check Control | check-control | String | Presence of active warning messages |
-| Plug Connection Status | plug-connection | String | Only available for phev, bev_rex and bev |
-| Charging Status | charge | String | Only available for phev, bev_rex and bev |
-| Last Status Timestamp | last-update | DateTime | Date and time of last status update |
-| Last Status Update Reason | last-update-reason | DateTime | Date and time of last status update |
-
-Overall Door Status values
-
-* _Closed_ - all doors closed
-* _Open_ - at least one door is open
-* _Undef_ - no door data delivered at all
-
-Overall Windows Status values
-
-* _Closed_ - all windows closed
-* _Open_ - at least one window is completely open
-* _Intermediate_ - at least one window is partially open
-* _Undef_ - no window data delivered at all
-
-Check Control values
-
-* _Active_ - at least one warning message is active
-* _Not Active_ - no warning message is active
-* _Undef_ - no data for warnings delivered
-
-Charging Status values
-
-* _Charging_
-* _Error_
-* _Finished Fully Charged_
-* _Finished Not Full_
-* _Invalid_
-* _Not Charging_
-* _Charging Goal reached_
-* _Waiting For Charging_
-
-Last update reasons
-
-* _CHARGING_DONE_
-* _CHARGING_INTERRUPED_
-* _CHARGING_PAUSED
-* _CHARGING_STARTED_
-* _CYCLIC_RECHARGING_
-* _DISCONNECTED_
-* _DOOR_STATE_CHANGED_
-* _NO_CYCLIC_RECHARGING_
-* _NO_LSC_TRIGGER_
-* _ON_DEMAND_
-* _PREDICTION_UPDATE_
-* _TEMPORARY_POWER_SUPPLY_FAILURE_
-* _UNKNOWN_
-* _VEHICLE_MOVING_
-* _VEHICLE_SECURED_
-* _VEHICLE_SHUTDOWN_
-* _VEHICLE_SHUTDOWN_SECURED_
-* _VEHICLE_UNSECURED_
-
-#### Services
-
-Group for all upcoming services with description, service date and/or service mileage.
-If more than one service is scheduled in the future the channel _name_ contains all future services as options.
-
-* Channel Group ID is **service**
-* Available for all vehicles
-* Read/Write access
-
-| Channel Label | Channel ID | Type | Access |
-|--------------------------------|---------------------|----------------|------------|
-| Service Name | name | String | Read/Write |
-| Service Details | details | String | Read |
-| Service Date | date | Number | Read |
-| Mileage till Service | mileage | Number:Length | Read |
-
-#### Check Control
-
-Group for all current active CheckControl messages.
-If more than one message is active the channel _name_ contains all active messages as options.
-
-* Channel Group ID is **check**
-* Available for all vehicles
-* Read/Write access
-
-| Channel Label | Channel ID | Type | Access |
-|---------------------------------|---------------------|----------------|------------|
-| CheckControl Description | name | String | Read/Write |
-| CheckControl Details | details | String | Read |
-| Mileage Occurrence | mileage | Number:Length | Read |
-
-#### Doors Details
-
-Detailed status of all doors and windows.
-
-* Channel Group ID is **doors**
-* Available for all vehicles if corresponding sensors are built-in
-* Read-only values
-
-| Channel Label | Channel ID | Type |
-|----------------------------|-------------------------|---------------|
-| Driver Door | driver-front | String |
-| Driver Door Rear | driver-rear | String |
-| Passenger Door | passenger-front | String |
-| Passenger Door Rear | passenger-rear | String |
-| Trunk | trunk | String |
-| Hood | hood | String |
-| Driver Window | win-driver-front | String |
-| Driver Rear Window | win-driver-rear | String |
-| Passenger Window | win-passenger-front | String |
-| Passenger Rear Window | win-passenger-rear | String |
-| Rear Window | win-rear | String |
-| Sunroof | sunroof | String |
-
-Possible states
-
-* _Undef_ - no status data available
-* _Invalid_ - this door / window isn't applicable for this vehicle
-* _Closed_ - the door / window is closed
-* _Open_ - the door / window is open
-* _Intermediate_ - window in intermediate position, not applicable for doors
-
-#### Range Data
-
-Based on vehicle type some channels are present or not.
-Conventional fuel vehicles don't provide *Electric Range* and battery electric vehicles don't show *Fuel Range*.
-Hybrid vehicles have both and in addition *Hybrid Range*.
-See description [Range vs Range Radius](#range-vs-range-radius) to get more information.
-
-* Channel Group ID is **range**
-* Availability according to table
-* Read-only values
-
-| Channel Label | Channel ID | Type | conv | phev | bev_rex | bev |
-|---------------------------|-------------------------|----------------------|------|------|---------|-----|
-| Mileage | mileage | Number:Length | X | X | X | X |
-| Fuel Range | range-fuel | Number:Length | X | X | X | |
-| Battery Range | range-electric | Number:Length | | X | X | X |
-| Max Battery Range | range-electric-max | Number:Length | | X | X | X |
-| Hybrid Range | range-hybrid | Number:Length | | X | X | |
-| Battery Charge Level | soc | Number:Dimensionless | | X | X | X |
-| Max Battery Capacity | soc-max | Number:Power | | | X | X | X |
-| Remaining Fuel | remaining-fuel | Number:Volume | X | X | X | |
-| Fuel Range Radius | range-radius-fuel | Number:Length | X | X | X | |
-| Electric Range Radius | range-radius-electric | Number:Length | | X | X | X |
-| Hybrid Range Radius | range-radius-hybrid | Number:Length | | X | X | |
-| Max Hybrid Range Radius | range-radius-hybrid-max | Number:Length | | X | X | |
-
-
-#### Charge Profile
-
-Charging options with date and time for preferred time windows and charging modes.
-
-* Channel Group ID is **charge**
-* Available for electric and hybrid vehicles
-* Read/Write access for UI. Use [Charge Profile Editing Action](#charge-profile-editing) in rules
-* There are 3 timers *T1, T2 and T3* available. Replace *X* with number 1,2 or 3 to target the correct timer
-* Additional override Timer *OT* defines a single departure besides the 3 predefined schedule timers
-
-| Channel Label | Channel Group ID | Channel ID | Type |
-|----------------------------|------------------|---------------------------|----------|
-| Charge Mode | charge | profile-mode | String |
-| Charge Preferences | charge | profile-prefs | String |
-| Window Start Time | charge | window-start | DateTime |
-| Window End Time | charge | window-end | DateTime |
-| A/C at Departure | charge | profile-climate | Switch |
-| T*X* Enabled | charge | timer*X*-enabled | Switch |
-| T*X* Departure Time | charge | timer*X*-departure | DateTime |
-| T*X* Days | charge | timer*X*-days | String |
-| T*X* Monday | charge | timer*X*-day-mon | Switch |
-| T*X* Tuesday | charge | timer*X*-day-tue | Switch |
-| T*X* Wednesday | charge | timer*X*-day-wed | Switch |
-| T*X* Thursday | charge | timer*X*-day-thu | Switch |
-| T*X* Friday | charge | timer*X*-day-fri | Switch |
-| T*X* Saturday | charge | timer*X*-day-sat | Switch |
-| T*X* Sunday | charge | timer*X*-day-sun | Switch |
-| OT Enabled | charge | override-enabled | Switch |
-| OT Departure Time | charge | override-departure | DateTime |
-
-The channel _profile-mode_ supports
-
-* *IMMEDIATE_CHARGING*
-* *DELAYED_CHARGING*
-
-The channel _profile-prefs_ supports
-
-* *NO_PRESELECTION*
-* *CHARGING_WINDOW*
-
-#### Location
-
-GPS location and heading of the vehicle.
-
-* Channel Group ID is **location**
-* Available for all vehicles with built-in GPS sensor. Function can be enabled/disabled in the head unit
-* Read-only values
-
-| Channel Label | Channel ID | Type |
-|-----------------|---------------------|--------------|
-| GPS Coordinates | gps | Location |
-| Heading | heading | Number:Angle |
-
-#### Last Trip
-
-Statistic values of duration, distance and consumption of the last trip.
-
-* Channel Group ID is **last-trip**
-* Available if *Statistics* is present in *Services Supported*. See [Vehicle Properties](#properties) for further details
-* Read-only values
-* Depending on units configuration in [Thing Configuration](#thing-configuration) average values are given for 100 kilometers or miles
-
-| Channel Label | Channel ID | Type |
-|-----------------------------------------|------------------------------|---------------|
-| Last Trip Date | date | DateTime |
-| Last Trip Duration | duration | Number:Time |
-| Last Trip Distance | distance | Number:Length |
-| Distance since Charge | distance-since-charging | Number:Length |
-| Avg. Power Consumption | avg-consumption | Number:Power |
-| Avg. Power Recuperation | avg-recuperation | Number:Power |
-| Avg. Combined Consumption | avg-combined-consumption | Number:Volume |
-
-
-#### Lifetime Statistics
-
-Providing lifetime consumption values.
-
-* Channel Group ID is **lifetime**
-* Available if *Statistics* is present in *Services Supported*. See [Vehicle Properties](#properties) for further details
-* Read-only values
-* Depending on units configuration in [Thing Configuration](#thing-configuration) average values are given for 100 kilometers or miles
-
-| Channel Label | Channel ID | Type |
-|-----------------------------------------|------------------------------|---------------|
-| Total Electric Distance | total-driven-distance | Number:Length |
-| Longest 1-Charge Distance | single-longest-distance | Number:Length |
-| Avg. Power Consumption | avg-consumption | Number:Power |
-| Avg. Power Recuperation | avg-recuperation | Number:Power |
-| Avg. Combined Consumption | avg-combined-consumption | Number:Volume |
-
-
-#### Remote Services
-
-Remote control of the vehicle.
-Send a *command* to the vehicle and the *state* is reporting the execution progress.
-Only one command can be executed each time.
-Parallel execution isn't supported.
-
-* Channel Group ID is **remote**
-* Available for all commands mentioned in *Services Activated*. See [Vehicle Properties](#properties) for further details
-* Read/Write access
-
-
-| Channel Label | Channel ID | Type | Access |
-|-------------------------|---------------------|---------|--------|
-| Remote Service Command | command | String | Write |
-| Service Execution State | state | String | Read |
-
-The channel _command_ provides options
-
-* _Flash Lights_
-* _Vehicle Finder_
-* _Door Lock_
-* _Door Unlock_
-* _Horn Blow_
-* _Climate Control_
-* _Start Charging_
-* _Send Charging Profile_
-
-The channel _state_ shows the progress of the command execution in the following order
-
-1) _Initiated_
-2) _Pending_
-3) _Delivered_
-4) _Executed_
-
-#### Destinations
-
-Shows the last destinations stored in the navigation system.
-If several last destinations are stored in the navigation system the channel _name_ contains all addresses as options.
-
-* Channel Group ID is **destination**
-* Available if *LastDestinations* is present in *Services Supported*. Check [Vehicle Properties](#properties) for further details
-* Read/Write access
-
-
-| Channel Label | Channel ID | Type | Access |
-|----------------------|---------------|-----------|-------------|
-| Name | name | String | Read/Write |
-| GPS Coordinates | gps | Location | Read |
-
-
-
-#### Image
-
-Image representation of the vehicle. Size and viewport are writable and can be
-The possible values are the same mentioned in [Thing Configuration](#thing-configuration).
-
-* Channel Group ID is **image**
-* Available for all vehicles
-* Read/Write access
-
-| Channel Label | Channel ID | Type | Access |
-|----------------------------|---------------------|--------|----------|
-| Rendered Vehicle Image | png | Image | Read |
-| Image Viewport | view | String | Write |
-| Image Picture Size | size | Number | Write |
-
-## Actions
-
-Get the _Actions_ object for your vehicle using the Thing ID
-
-* bmwconnecteddrive - Binding ID, don't change!
-* bev_rex - [Thing UID](#things) of your car
-* user - Thing ID of the [Bridge](#bridge)
-* i3 - Thing ID of your car
-
-```
- val profile = getActions("bmwconnecteddrive", "bmwconnecteddrive:bev_rex:user:i3")
-```
-
-### Charge Profile Editing
-
-Like in the Charge Profile Channels 3 Timers are provided. Replace *X* with 1, 2 or 3 to address the right timer.
-
-| Function | Parameters | Returns | Description |
-|---------------------------------------|------------------|---------------------------|------------------------------------------------------------|
-| getClimatizationEnabled | void | Boolean | Returns the enabled state of climatization |
-| setClimatizationEnabled | Boolean | void | Sets the enabled state of climatization |
-| getChargingMode | void | String | Gets the charging-mode, see valid options below |
-| setChargingMode | String | void | Sets the charging-mode, see valid options below |
-| getPreferredWindowStart | void | LocalTime | Returns the preferred charging-window start time |
-| setPreferredWindowStart | LocalTime | void | Sets the preferred charging-window start time |
-| getPreferredWindowEnd | void | LocalTime | Returns the preferred charging-window end time |
-| setPreferredWindowEnd | LocalTime | void | Sets the preferred charging-window end time |
-| getTimer*X*Enabled | void | Boolean | Returns the enabled state of timer*X* |
-| setTimer*X*Enabled | Boolean | void | Returns the enabled state of timer*X* |
-| getTimer*X*Departure | void | LocalTime | Returns the departure time of timer*X* |
-| setTimer*X*Departure | LocalTime | void | Sets the timer*X* departure time |
-| getTimer*X*Days | void | Set<DayOfWeek> | Returns the days of week timer*X* is enabled for |
-| setTimer*X*Days | Set<DayOfWeek> | void | sets the days of week timer*X* is enabled for |
-| getOverrideTimerEnabled | void | Boolean | Returns the enabled state of override timer |
-| setOverrideTimerEnabled | Boolean | void | Sets the enabled state of override timer |
-| getOverrideTimerDeparture | void | LocalTime | Returns the departure time of override timer |
-| setOverrideTimerDeparture | LocalTime | void | Sets the override timer departure time |
-| getOverrideTimerDays | void | Set<DayOfWeek> | Returns the days of week the overrideTimer is enabled for |
-| setOverrideTimerDays | Set<DayOfWeek> | void | Sets the days of week the overrideTimer is enabled for |
-| cancelEditChargeProfile | void | void | Cancel current edit of charging profile |
-| sendChargeProfile | void | void | Sends the charging profile to the vehicle |
-
-Values for valid charging mode get/set
-
-* *IMMEDIATE_CHARGING*
-* *DELAYED_CHARGING*
-
-
-## Further Descriptions
-
-### Dynamic Data
-
-<img align="right" src="./doc/ServiceOptions.png" width="400" height="350"/>
-
-There are 3 occurrences of dynamic data delivered
-
-* Upcoming Services delivered in group [Services](#services)
-* Check Control Messages delivered in group [Check Control](#check-control)
-* Last Destinations delivered in group [Destinations](#destinations)
-
-The channel id _name_ shows the first element as default.
-All other possibilities are attached as options.
-The picture on the right shows the _Service Name_ item and all four possible options.
-Select the desired service and the corresponding _Service Date & Milage_ will be shown.
-
-### TroubleShooting
-
-BMW has a high range of vehicles supported by ConnectedDrive.
-In case of any issues with this binding help to resolve it!
-Please perform the following steps:
-
-* Can you [log into ConnectedDrive](https://www.bmw-connecteddrive.com/country-region-select/country-region-selection.html) with your credentials? Please note this isn't the BMW Customer portal - it's the ConnectedDrive portal
-* Is the vehicle listed in your account? There's a one-to-one relation from user to vehicle
-
-If the access to the portal is working and the vehicle is listed some debug data is needed in order to identify the issue.
-
-#### Generate Debug Fingerprint
-
-If you checked the above pre-conditions you need to get the debug fingerprint from the logs.
-First [enable debug logging](https://www.openhab.org/docs/administration/logging.html#defining-what-to-log) for the binding.
-
-```
-log:set DEBUG org.openhab.binding.bmwconnecteddrive
-```
-
-The debug fingerprint is generated immediately after the vehicle thing is initialized the first time, e.g. after openHAB startup.
-To force a new fingerprint disable the thing shortly and enable it again.
-Personal data is eliminated from the log entries so it should be possible to share them in public.
-Data like
-
-* Dealer Properties
-* Vehicle Identification Number (VIN)
-* Location latitude / longitude
-
-are anonymized.
-You'll find the fingerprint in the logs with the command
-
-```
-grep "Troubleshoot Fingerprint Data" openhab.log
-```
-
-After the corresponding fingerprint is generated please [follow the instructions to raise an issue](https://community.openhab.org/t/how-to-file-an-issue/68464) and attach the fingerprint data!
-Your feedback is highly appreciated!
-
-
-### Range vs Range Radius
-
-<img align="right" src="./doc/range-radius.png" width="400" height="350"/>
-
-You will observe differences in the vehicle range and range radius values.
-While range is indicating the possible distance to be driven on roads the range radius indicates the reachable range on the map.
-
-The right picture shows the distance between Kassel and Frankfurt in Germany.
-While the air-line distance is ~145 kilometer the route distance is ~192 kilometer.
-So range value is the normal remaining range while the range radius values can be used e.g. on [Mapview](https://www.openhab.org/docs/configuration/sitemaps.html#element-type-mapview) to indicate the reachable range on map.
-Please note this is just an indicator of the effective range.
-Especially for electric vehicles it depends on many factors like driving style and usage of electric consumers.
-
-## Full Example
-
-The example is based on a BMW i3 with range extender (REX).
-Exchange the three configuration parameters in the Things section
-
-* YOUR_USERNAME - with your ConnectedDrive login username
-* YOUR_PASSWORD - with your ConnectedDrive password credentials
-* VEHICLE_VIN - the vehicle identification number
-
-In addition search for all occurrences of *i3* and replace it with your Vehicle Identification like *x3* or *535d* and you're ready to go!
-
-### Things File
-
-```
-Bridge bmwconnecteddrive:account:user "BMW ConnectedDrive Account" [userName="YOUR_USERNAME",password="YOUR_PASSWORD",region="ROW"] {
- Thing bev_rex i3 "BMW i3 94h REX" [ vin="VEHICLE_VIN",units="AUTODETECT",imageSize=600,imageViewport="FRONT",refreshInterval=5]
-}
-```
-
-### Items File
-
-```
-Number:Length i3Mileage "Odometer [%d %unit%]" <line> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:range#mileage" }
-Number:Length i3Range "Range [%d %unit%]" <motion> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:range#hybrid"}
-Number:Length i3RangeElectric "Electric Range [%d %unit%]" <motion> (i3,long) {channel="bmwconnecteddrive:bev_rex:user:i3:range#electric"}
-Number:Length i3RangeFuel "Fuel Range [%d %unit%]" <motion> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:range#fuel"}
-Number:Dimensionless i3BatterySoc "Battery Charge [%.1f %%]" <battery> (i3,long) {channel="bmwconnecteddrive:bev_rex:user:i3:range#soc"}
-Number:Volume i3Fuel "Fuel [%.1f %unit%]" <oil> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:range#remaining-fuel"}
-Number:Length i3RadiusElectric "Electric Radius [%d %unit%]" <zoom> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:range#radius-electric" }
-Number:Length i3RadiusHybrid "Hybrid Radius [%d %unit%]" <zoom> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:range#radius-hybrid" }
-
-String i3DoorStatus "Door Status [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:status#doors" }
-String i3WindowStatus "Window Status [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:status#windows" }
-String i3LockStatus "Lock Status [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:status#lock" }
-DateTime i3NextServiceDate "Next Service Date [%1$tb %1$tY]" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:status#service-date" }
-String i3NextServiceMileage "Next Service Mileage [%d %unit%]" <line> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:status#service-mileage" }
-String i3CheckControl "Check Control [%s]" <error> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:status#check-control" }
-String i3ChargingStatus "Charging [%s]" <energy> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:status#charge" }
-DateTime i3LastUpdate "Update [%1$tA, %1$td.%1$tm. %1$tH:%1$tM]" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:status#last-update"}
-
-DateTime i3TripDateTime "Trip Date [%1$tA, %1$td.%1$tm. %1$tH:%1$tM]" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:last-trip#date"}
-Number:Time i3TripDuration "Trip Duration [%d %unit%]" <time> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:last-trip#duration"}
-Number:Length i3TripDistance "Distance [%d %unit%]" <line> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:last-trip#distance" }
-Number:Length i3TripDistanceSinceCharge "Distance since last Charge [%d %unit%]" <line> (i3,long) {channel="bmwconnecteddrive:bev_rex:user:i3:last-trip#distance-since-charging" }
-Number:Energy i3AvgTripConsumption "Average Consumption [%.1f %unit%]" <energy> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:last-trip#avg-consumption" }
-Number:Volume i3AvgTripCombined "Average Combined Consumption [%.1f %unit%]" <oil> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:last-trip#avg-combined-consumption" }
-Number:Energy i3AvgTripRecuperation "Average Recuperation [%.1f %unit%]" <energy> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:last-trip#avg-recuperation" }
-
-Number:Length i3TotalElectric "Electric Distance Driven [%d %unit%]" <line> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:lifetime#total-driven-distance" }
-Number:Length i3LongestEVTrip "Longest Electric Trip [%d %unit%]" <line> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:lifetime#single-longest-distance" }
-Number:Energy i3AvgConsumption "Average Consumption [%.1f %unit%]" <energy> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:lifetime#avg-consumption" }
-Number:Volume i3AvgCombined "Average Combined Consumption [%.1f %unit%]" <oil> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:lifetime#avg-combined-consumption" }
-Number:Energy i3AvgRecuperation "Average Recuperation [%.1f %unit%]" <energy> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:lifetime#avg-recuperation" }
-
-Location i3Location "Location [%s]" <zoom> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:location#gps" }
-Number:Angle i3Heading "Heading [%.1f %unit%]" <zoom> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:location#heading" }
-
-String i3RemoteCommand "Command [%s]" <switch> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:remote#command" }
-String i3RemoteState "Remote Execution State [%s]" <status> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:remote#state" }
-
-String i3DriverDoor "Driver Door [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#driver-front" }
-String i3DriverDoorRear "Driver Door Rear [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#driver-rear" }
-String i3PassengerDoor "Passenger Door [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#passenger-front" }
-String i3PassengerDoorRear "Passenger Door Rear [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#passenger-rear" }
-String i3Hood "Hood [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#hood" }
-String i3Trunk "Trunk [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#trunk" }
-String i3DriverWindow "Driver Window [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#win-driver-front" }
-String i3DriverWindowRear "Driver Window Rear [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#win-driver-rear" }
-String i3PassengerWindow "Passenger Window [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#win-passenger-front" }
-String i3PassengerWindowRear "Passenger Window Rear [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#win-passenger-rear" }
-String i3RearWindow "Rear Window [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#win-rear" }
-String i3Sunroof "Sunroof [%s]" <lock> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:doors#sunroof" }
-
-String i3ServiceName "Service Name [%s]" <text> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:service#name" }
-String i3ServiceDetails "Service Details [%s]" <text> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:service#details" }
-Number:Length i3ServiceMileage "Service Mileage [%d %unit%]" <line> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:service#mileage" }
-DateTime i3ServiceDate "Service Date [%1$tb %1$tY]" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:service#date" }
-
-String i3CCName "CheckControl Name [%s]" <text> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:check#name" }
-String i3CCDetails "CheckControl Details [%s]" <text> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:check#details" }
-Number:Length i3CCMileage "CheckControl Mileage [%d %unit%]" <line> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:check#mileage" }
-
-String i3DestName "Destination [%s]" <house> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:destination#name" }
-Location i3DestLocation "GPS [%s]" <zoom> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:destination#gps" }
-
-Switch i3ChargeProfileClimate "Charge Profile Climatization" <temperature> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#profile-climate" }
-String i3ChargeProfileMode "Charge Profile Mode [%s]" <energy> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#profile-mode" }
-DateTime i3ChargeWindowStart "Charge Window Start [%1$tH:%1$tM]" <time> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#window-start" }
-Number i3ChargeWindowStartHour "Charge Window Start Hour [%d]" <time> (i3)
-Number i3ChargeWindowStartMinute "Charge Window Start Minute [%d]" <time> (i3)
-DateTime i3ChargeWindowEnd "Charge Window End [%1$tH:%1$tM]" <time> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#window-end" }
-Number i3ChargeWindowEndHour "Charge Window End Hour [%d]" <time> (i3)
-Number i3ChargeWindowEndMinute "Charge Window End Minute [%d]" <time> (i3)
-DateTime i3Timer1Departure "Timer 1 Departure [%1$tH:%1$tM]" <time> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer1-departure" }
-Number i3Timer1DepartureHour "Timer 1 Departure Hour [%d]" <time> (i3)
-Number i3Timer1DepartureMinute "Timer 1 Departure Minute [%d]" <time> (i3)
-String i3Timer1Days "Timer 1 Days [%s]" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer1-days" }
-Switch i3Timer1DayMon "Timer 1 Monday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer1-day-mon" }
-Switch i3Timer1DayTue "Timer 1 Tuesday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer1-day-tue" }
-Switch i3Timer1DayWed "Timer 1 Wednesday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer1-day-wed" }
-Switch i3Timer1DayThu "Timer 1 Thursday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer1-day-thu" }
-Switch i3Timer1DayFri "Timer 1 Friday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer1-day-fri" }
-Switch i3Timer1DaySat "Timer 1 Saturday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer1-day-sat" }
-Switch i3Timer1DaySun "Timer 1 Sunday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer1-day-sun" }
-Switch i3Timer1Enabled "Timer 1 Enabled" <switch> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer1-enabled" }
-DateTime i3Timer2Departure "Timer 2 Departure [%1$tH:%1$tM]" <time> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer2-departure" }
-Number i3Timer2DepartureHour "Timer 2 Departure Hour [%d]" <time> (i3)
-Number i3Timer2DepartureMinute "Timer 2 Departure Minute [%d]" <time> (i3)
-String i3Timer2Days "Timer 2 Days [%s]" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer2-days" }
-Switch i3Timer2DayMon "Timer 2 Monday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer2-day-mon" }
-Switch i3Timer2DayTue "Timer 2 Tuesday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer2-day-tue" }
-Switch i3Timer2DayWed "Timer 2 Wednesday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer2-day-wed" }
-Switch i3Timer2DayThu "Timer 2 Thursday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer2-day-thu" }
-Switch i3Timer2DayFri "Timer 2 Friday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer2-day-fri" }
-Switch i3Timer2DaySat "Timer 2 Saturday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer2-day-sat" }
-Switch i3Timer2DaySun "Timer 2 Sunday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer2-day-sun" }
-Switch i3Timer2Enabled "Timer 2 Enabled" <switch> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer2-enabled" }
-DateTime i3Timer3Departure "Timer 3 Departure [%1$tH:%1$tM]" <time> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer3-departure" }
-Number i3Timer3DepartureHour "Timer 3 Departure Hour [%d]" <time> (i3)
-Number i3Timer3DepartureMinute "Timer 3 Departure Minute [%d]" <time> (i3)
-String i3Timer3Days "Timer 3 Days [%s]" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer3-days" }
-Switch i3Timer3DayMon "Timer 3 Monday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer3-day-mon" }
-Switch i3Timer3DayTue "Timer 3 Tuesday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer3-day-tue" }
-Switch i3Timer3DayWed "Timer 3 Wednesday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer3-day-wed" }
-Switch i3Timer3DayThu "Timer 3 Thursday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer3-day-thu" }
-Switch i3Timer3DayFri "Timer 3 Friday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer3-day-fri" }
-Switch i3Timer3DaySat "Timer 3 Saturday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer3-day-sat" }
-Switch i3Timer3DaySun "Timer 3 Sunday" <calendar> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer3-day-sun" }
-Switch i3Timer3Enabled "Timer 3 Enabled" <switch> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#timer3-enabled" }
-Switch i3OverrideEnabled "Override Timer Enabled" <switch> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#override-enabled"}
-DateTime i3OverrideDeparture "Override Timer Departure [%1$tH:%1$tM]" <time> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:charge#override-departure" }
-Number i3OverrideDepartureHour "Override Timer Departure Hour [%d]" <time> (i3)
-Number i3OverrideDepartureMinute "Override Timer Departure Minute [%d]" <time> (i3)
-
-Image i3Image "Image" (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:image#png" }
-String i3ImageViewport "Image Viewport [%s]" <zoom> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:image#view" }
-Number i3ImageSize "Image Size [%d]" <zoom> (i3) {channel="bmwconnecteddrive:bev_rex:user:i3:image#size" }
-```
-
-### Sitemap File
-
-```
-sitemap BMW label="BMW" {
- Frame label="BMW i3" {
- Image item=i3Image
-
- }
- Frame label="Range" {
- Text item=i3Mileage
- Text item=i3Range
- Text item=i3RangeElectric
- Text item=i3RangeFuel
- Text item=i3BatterySoc
- Text item=i3Fuel
- Text item=i3RadiusElectric
- Text item=i3RadiusHybrid
- }
- Frame label="Status" {
- Text item=i3DoorStatus
- Text item=i3WindowStatus
- Text item=i3LockStatus
- Text item=i3NextServiceDate
- Text item=i3NextServiceMileage
- Text item=i3CheckControl
- Text item=i3ChargingStatus
- Text item=i3LastUpdate
- }
- Frame label="Remote Services" {
- Selection item=i3RemoteCommand
- Text item=i3RemoteState
- }
- Frame label="Last Trip" {
- Text item=i3TripDateTime
- Text item=i3TripDuration
- Text item=i3TripDistance
- Text item=i3TripDistanceSinceCharge
- Text item=i3AvgTripConsumption
- Text item=i3AvgTripRecuperation
- Text item=i3AvgTripCombined
- }
- Frame label="Lifetime" {
- Text item=i3TotalElectric
- Text item=i3LongestEVTrip
- Text item=i3AvgConsumption
- Text item=i3AvgRecuperation
- Text item=i3AvgCombined
- }
- Frame label="Services" {
- Text item=i3ServiceName
- Text item=i3ServiceMileage
- Text item=i3ServiceDate
- }
- Frame label="CheckControl" {
- Text item=i3CCName
- Text item=i3CCMileage
- }
- Frame label="Door Details" {
- Text item=i3DriverDoor visibility=[i3DriverDoor!="INVALID"]
- Text item=i3DriverDoorRear visibility=[i3DriverDoorRear!="INVALID"]
- Text item=i3PassengerDoor visibility=[i3PassengerDoor!="INVALID"]
- Text item=i3PassengerDoorRear visibility=[i3PassengerDoorRear!="INVALID"]
- Text item=i3Hood visibility=[i3Hood!="INVALID"]
- Text item=i3Trunk visibility=[i3Trunk!="INVALID"]
- Text item=i3DriverWindow visibility=[i3DriverWindow!="INVALID"]
- Text item=i3DriverWindowRear visibility=[i3DriverWindowRear!="INVALID"]
- Text item=i3PassengerWindow visibility=[i3PassengerWindow!="INVALID"]
- Text item=i3PassengerWindowRear visibility=[i3PassengerWindowRear!="INVALID"]
- Text item=i3RearWindow visibility=[i3RearWindow!="INVALID"]
- Text item=i3Sunroof visibility=[i3Sunroof!="INVALID"]
- }
- Frame label="Location" {
- Text item=i3Location
- Text item=i3Heading
- }
- Frame label="Charge Profile" {
- Switch item=i3ChargeProfileClimate
- Selection item=i3ChargeProfileMode
- Text item=i3ChargeWindowStart
- Setpoint item=i3ChargeWindowStartHour maxValue=23 step=1 icon="time"
- Setpoint item=i3ChargeWindowStartMinute maxValue=55 step=5 icon="time"
- Text item=i3ChargeWindowEnd
- Setpoint item=i3ChargeWindowEndHour maxValue=23 step=1 icon="time"
- Setpoint item=i3ChargeWindowEndMinute maxValue=55 step=5 icon="time"
- Text item=i3Timer1Departure
- Setpoint item=i3Timer1DepartureHour maxValue=23 step=1 icon="time"
- Setpoint item=i3Timer1DepartureMinute maxValue=55 step=5 icon="time"
- Text item=i3Timer1Days
- Switch item=i3Timer1DayMon
- Switch item=i3Timer1DayTue
- Switch item=i3Timer1DayWed
- Switch item=i3Timer1DayThu
- Switch item=i3Timer1DayFri
- Switch item=i3Timer1DaySat
- Switch item=i3Timer1DaySun
- Switch item=i3Timer1Enabled
- Text item=i3Timer2Departure
- Setpoint item=i3Timer2DepartureHour maxValue=23 step=1 icon="time"
- Setpoint item=i3Timer2DepartureMinute maxValue=55 step=5 icon="time"
- Text item=i3Timer2Days
- Switch item=i3Timer2DayMon
- Switch item=i3Timer2DayTue
- Switch item=i3Timer2DayWed
- Switch item=i3Timer2DayThu
- Switch item=i3Timer2DayFri
- Switch item=i3Timer2DaySat
- Switch item=i3Timer2DaySun
- Switch item=i3Timer2Enabled
- Text item=i3Timer3Departure
- Setpoint item=i3Timer3DepartureHour maxValue=23 step=1 icon="time"
- Setpoint item=i3Timer3DepartureMinute maxValue=55 step=5 icon="time"
- Text item=i3Timer3Days
- Switch item=i3Timer3DayMon
- Switch item=i3Timer3DayTue
- Switch item=i3Timer3DayWed
- Switch item=i3Timer3DayThu
- Switch item=i3Timer3DayFri
- Switch item=i3Timer3DaySat
- Switch item=i3Timer3DaySun
- Switch item=i3Timer3Enabled
- Switch item=i3OverrideEnabled
- Text item=i3OverrideDeparture
- Setpoint item=i3OverrideDepartureHour maxValue=23 step=1 icon="time"
- Setpoint item=i3OverrideDepartureMinute maxValue=55 step=5 icon="time"
- }
- Frame label="Last Destinations" {
- Text item=i3DestName
- Text item=i3DestLocation
- }
- Frame label="Image Properties" {
- Text item=i3ImageViewport
- Text item=i3ImageSize
- }
-}
-```
-
-### Rules File
-
-```
-rule "i3ChargeWindowStartSetpoint"
-when
- Item i3ChargeWindowStartMinute changed or
- Item i3ChargeWindowStartHour changed
-then
- val hour = (i3ChargeWindowStartHour.state as Number).intValue
- val minute = (i3ChargeWindowStartMinute.state as Number).intValue
- val time = (i3ChargeWindowStart.state as DateTimeType).zonedDateTime
- i3ChargeWindowStart.sendCommand(new DateTimeType(time.withHour(hour).withMinute(minute)))
-end
-
-rule "i3ChargeWindowStart"
-when
- Item i3ChargeWindowStart changed
-then
- val time = (i3ChargeWindowStart.state as DateTimeType).zonedDateTime
- i3ChargeWindowStartMinute.sendCommand(time.minute)
- i3ChargeWindowStartHour.sendCommand(time.hour)
-end
-
-rule "i3ChargeWindowEndSetpoint"
-when
- Item i3ChargeWindowEndMinute changed or
- Item i3ChargeWindowEndHour changed
-then
- val hour = (i3ChargeWindowEndHour.state as Number).intValue
- val minute = (i3ChargeWindowEndMinute.state as Number).intValue
- val time = (i3ChargeWindowEnd.state as DateTimeType).zonedDateTime
- i3ChargeWindowEnd.sendCommand(new DateTimeType(time.withHour(hour).withMinute(minute)))
-end
-
-rule "i3ChargeWindowEnd"
-when
- Item i3ChargeWindowEnd changed
-then
- val time = (i3ChargeWindowEnd.state as DateTimeType).zonedDateTime
- i3ChargeWindowEndMinute.sendCommand(time.minute)
- i3ChargeWindowEndHour.sendCommand(time.hour)
-end
-
-rule "i3Timer1DepartureSetpoint"
-when
- Item i3Timer1DepartureMinute changed or
- Item i3Timer1DepartureHour changed
-then
- val hour = (i3Timer1DepartureHour.state as Number).intValue
- val minute = (i3Timer1DepartureMinute.state as Number).intValue
- val time = (i3Timer1Departure.state as DateTimeType).zonedDateTime
- i3Timer1Departure.sendCommand(new DateTimeType(time.withHour(hour).withMinute(minute)))
-end
-
-rule "i3Timer1Departure"
-when
- Item i3Timer1Departure changed
-then
- val time = (i3Timer1Departure.state as DateTimeType).zonedDateTime
- i3Timer1DepartureMinute.sendCommand(time.minute)
- i3Timer1DepartureHour.sendCommand(time.hour)
-end
-
-rule "i3Timer2DepartureSetpoint"
-when
- Item i3Timer2DepartureMinute changed or
- Item i3Timer2DepartureHour changed
-then
- val hour = (i3Timer2DepartureHour.state as Number).intValue
- val minute = (i3Timer2DepartureMinute.state as Number).intValue
- val time = (i3Timer2Departure.state as DateTimeType).zonedDateTime
- i3Timer2Departure.sendCommand(new DateTimeType(time.withHour(hour).withMinute(minute)))
-end
-
-rule "i3Timer2Departure"
-when
- Item i3Timer2Departure changed
-then
- val time = (i3Timer2Departure.state as DateTimeType).zonedDateTime
- i3Timer2DepartureMinute.sendCommand(time.minute)
- i3Timer2DepartureHour.sendCommand(time.hour)
-end
-
-rule "i3Timer3DepartureSetpoint"
-when
- Item i3Timer3DepartureMinute changed or
- Item i3Timer3DepartureHour changed
-then
- val hour = (i3Timer3DepartureHour.state as Number).intValue
- val minute = (i3Timer3DepartureMinute.state as Number).intValue
- val time = (i3Timer3Departure.state as DateTimeType).zonedDateTime
- i3Timer3Departure.sendCommand(new DateTimeType(time.withHour(hour).withMinute(minute)))
-end
-
-rule "i3Timer3Departure"
-when
- Item i3Timer3Departure changed
-then
- val time = (i3Timer3Departure.state as DateTimeType).zonedDateTime
- i3Timer3DepartureMinute.sendCommand(time.minute)
- i3Timer3DepartureHour.sendCommand(time.hour)
-end
-
-rule "i3OverrideDepartureSetpoint"
-when
- Item i3OverrideDepartureMinute changed or
- Item i3OverrideDepartureHour changed
-then
- val hour = (i3OverrideDepartureHour.state as Number).intValue
- val minute = (i3OverrideDepartureMinute.state as Number).intValue
- val time = (i3OverrideDeparture.state as DateTimeType).zonedDateTime
- i3OverrideDeparture.sendCommand(new DateTimeType(time.withHour(hour).withMinute(minute)))
-end
-
-rule "i3OverrideDeparture"
-when
- Item i3OverrideDeparture changed
-then
- val time = (i3OverrideDeparture.state as DateTimeType).zonedDateTime
- i3OverrideDepartureMinute.sendCommand(time.minute)
- i3OverrideDepartureHour.sendCommand(time.hour)
-end
-```
-
-### Action example
-
-```
- val profile = getActions("bmwconnecteddrive", "bmwconnecteddrive:bev_rex:user:i3")
- val now = ZonedDateTime.now.toLocalTime
- profile.setChargingMode("DELAYED_CHARGING")
- profile.setTimer1Departure(now.minusHours(2))
- profile.setTimer1Days(java.util.Set())
- profile.setTimer1Enabled(true)
- profile.setTimer2Enabled(false)
- profile.setTimer3Enabled(false)
- profile.setPreferredWindowStart(now.minusHours(6))
- profile.setPreferredWindowEnd(now.minusHours(2))
- profile.sendChargeProfile()
-```
-
-## Credits
-
-This work is based on the project of [Bimmer Connected](https://github.com/bimmerconnected/bimmer_connected).
-Also a [manual installation based on python](https://community.openhab.org/t/script-to-access-the-bmw-connecteddrive-portal-via-oh/37345) was already available for openHAB.
-This binding is basically a port to openHAB based on these concept works!
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
-
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.openhab.addons.bundles</groupId>
- <artifactId>org.openhab.addons.reactor.bundles</artifactId>
- <version>3.3.0-SNAPSHOT</version>
- </parent>
-
- <artifactId>org.openhab.binding.bmwconnecteddrive</artifactId>
-
- <name>openHAB Add-ons :: Bundles :: BMWConnectedDrive Binding</name>
-
-</project>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<features name="org.openhab.binding.bmwconnecteddrive-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
- <repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${project.version}/xml/features</repository>
-
- <feature name="openhab-binding-bmwconnecteddrive" description="BMWConnectedDrive Binding" version="${project.version}">
- <feature>openhab-runtime-base</feature>
- <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.bmwconnecteddrive/${project.version}</bundle>
- </feature>
-</features>
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-
-/**
- * The {@link ConnectedDriveConfiguration} class contains fields mapping thing configuration parameters.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class ConnectedDriveConfiguration {
-
- /**
- * Depending on the location the correct server needs to be called
- */
- public String region = Constants.EMPTY;
-
- /**
- * BMW Connected Drive Username
- */
- public String userName = Constants.EMPTY;
-
- /**
- * BMW Connected Drive Password
- */
- public String password = Constants.EMPTY;
-
- /**
- * Prefer MyBMW API instead of BMW Connected Drive
- */
- public boolean preferMyBmw = false;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal;
-
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.thing.ThingTypeUID;
-
-/**
- * The {@link ConnectedDriveConstants} class defines common constants, which are
- * used across the whole binding.
- *
- * @author Bernd Weymann - Initial contribution
- * @author Norbert Truchsess - edit & send of charge profile
- */
-@NonNullByDefault
-public class ConnectedDriveConstants {
-
- private static final String BINDING_ID = "bmwconnecteddrive";
-
- // Units
- public static final String UNITS_AUTODETECT = "AUTODETECT";
- public static final String UNITS_IMPERIAL = "IMPERIAL";
- public static final String UNITS_METRIC = "METRIC";
-
- public static final String VIN = "vin";
-
- public static final int DEFAULT_IMAGE_SIZE_PX = 1024;
- public static final int DEFAULT_REFRESH_INTERVAL_MINUTES = 5;
- public static final String DEFAULT_IMAGE_VIEWPORT = "FRONT";
-
- // See constants from bimmer-connected
- // https://github.com/bimmerconnected/bimmer_connected/blob/master/bimmer_connected/vehicle.py
- public enum VehicleType {
- CONVENTIONAL("conv"),
- PLUGIN_HYBRID("phev"),
- ELECTRIC_REX("bev_rex"),
- ELECTRIC("bev");
-
- private final String type;
-
- VehicleType(String s) {
- type = s;
- }
-
- @Override
- public String toString() {
- return type;
- }
- }
-
- public enum ChargingMode {
- IMMEDIATE_CHARGING,
- DELAYED_CHARGING
- }
-
- public enum ChargingPreference {
- NO_PRESELECTION,
- CHARGING_WINDOW
- }
-
- public static final Set<String> FUEL_VEHICLES = Set.of(VehicleType.CONVENTIONAL.toString(),
- VehicleType.PLUGIN_HYBRID.toString(), VehicleType.ELECTRIC_REX.toString());
- public static final Set<String> ELECTRIC_VEHICLES = Set.of(VehicleType.ELECTRIC.toString(),
- VehicleType.PLUGIN_HYBRID.toString(), VehicleType.ELECTRIC_REX.toString());
-
- // Countries with Mileage display
- public static final Set<String> IMPERIAL_COUNTRIES = Set.of("US", "GB");
-
- // List of all Thing Type UIDs
- public static final ThingTypeUID THING_TYPE_CONNECTED_DRIVE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
- public static final ThingTypeUID THING_TYPE_CONV = new ThingTypeUID(BINDING_ID,
- VehicleType.CONVENTIONAL.toString());
- public static final ThingTypeUID THING_TYPE_PHEV = new ThingTypeUID(BINDING_ID,
- VehicleType.PLUGIN_HYBRID.toString());
- public static final ThingTypeUID THING_TYPE_BEV_REX = new ThingTypeUID(BINDING_ID,
- VehicleType.ELECTRIC_REX.toString());
- public static final ThingTypeUID THING_TYPE_BEV = new ThingTypeUID(BINDING_ID, VehicleType.ELECTRIC.toString());
- public static final Set<ThingTypeUID> SUPPORTED_THING_SET = Set.of(THING_TYPE_CONNECTED_DRIVE_ACCOUNT,
- THING_TYPE_CONV, THING_TYPE_PHEV, THING_TYPE_BEV_REX, THING_TYPE_BEV);
-
- // Thing Group definitions
- public static final String CHANNEL_GROUP_STATUS = "status";
- public static final String CHANNEL_GROUP_SERVICE = "service";
- public static final String CHANNEL_GROUP_CHECK_CONTROL = "check";
- public static final String CHANNEL_GROUP_DOORS = "doors";
- public static final String CHANNEL_GROUP_RANGE = "range";
- public static final String CHANNEL_GROUP_LOCATION = "location";
- public static final String CHANNEL_GROUP_LAST_TRIP = "last-trip";
- public static final String CHANNEL_GROUP_LIFETIME = "lifetime";
- public static final String CHANNEL_GROUP_REMOTE = "remote";
- public static final String CHANNEL_GROUP_CHARGE = "charge";
- public static final String CHANNEL_GROUP_VEHICLE_IMAGE = "image";
- public static final String CHANNEL_GROUP_DESTINATION = "destination";
-
- // Generic Constants for several groups
- public static final String NAME = "name";
- public static final String DETAILS = "details";
- public static final String DATE = "date";
- public static final String MILEAGE = "mileage";
- public static final String GPS = "gps";
- public static final String HEADING = "heading";
-
- // Status
- public static final String DOORS = "doors";
- public static final String WINDOWS = "windows";
- public static final String LOCK = "lock";
- public static final String SERVICE_DATE = "service-date";
- public static final String SERVICE_MILEAGE = "service-mileage";
- public static final String CHECK_CONTROL = "check-control";
- public static final String PLUG_CONNECTION = "plug-connection";
- public static final String CHARGE_STATUS = "charge";
- public static final String CHARGE_END_REASON = "reason";
- public static final String CHARGE_REMAINING = "remaining";
- public static final String LAST_UPDATE = "last-update";
- public static final String LAST_UPDATE_REASON = "last-update-reason";
-
- // Door Details
- public static final String DOOR_DRIVER_FRONT = "driver-front";
- public static final String DOOR_DRIVER_REAR = "driver-rear";
- public static final String DOOR_PASSENGER_FRONT = "passenger-front";
- public static final String DOOR_PASSENGER_REAR = "passenger-rear";
- public static final String HOOD = "hood";
- public static final String TRUNK = "trunk";
- public static final String WINDOW_DOOR_DRIVER_FRONT = "win-driver-front";
- public static final String WINDOW_DOOR_DRIVER_REAR = "win-driver-rear";
- public static final String WINDOW_DOOR_PASSENGER_FRONT = "win-passenger-front";
- public static final String WINDOW_DOOR_PASSENGER_REAR = "win-passenger-rear";
- public static final String WINDOW_REAR = "win-rear";
- public static final String SUNROOF = "sunroof";
-
- // Charge Profile
- public static final String CHARGE_PROFILE_CLIMATE = "profile-climate";
- public static final String CHARGE_PROFILE_MODE = "profile-mode";
- public static final String CHARGE_PROFILE_PREFERENCE = "profile-prefs";
- public static final String CHARGE_WINDOW_START = "window-start";
- public static final String CHARGE_WINDOW_END = "window-end";
- public static final String CHARGE_TIMER1 = "timer1";
- public static final String CHARGE_TIMER2 = "timer2";
- public static final String CHARGE_TIMER3 = "timer3";
- public static final String CHARGE_OVERRIDE = "override";
- public static final String CHARGE_DEPARTURE = "-departure";
- public static final String CHARGE_ENABLED = "-enabled";
- public static final String CHARGE_DAYS = "-days";
- public static final String CHARGE_DAY_MON = "-day-mon";
- public static final String CHARGE_DAY_TUE = "-day-tue";
- public static final String CHARGE_DAY_WED = "-day-wed";
- public static final String CHARGE_DAY_THU = "-day-thu";
- public static final String CHARGE_DAY_FRI = "-day-fri";
- public static final String CHARGE_DAY_SAT = "-day-sat";
- public static final String CHARGE_DAY_SUN = "-day-sun";
-
- // Range
- public static final String RANGE_HYBRID = "hybrid";
- public static final String RANGE_HYBRID_MAX = "hybrid-max";
- public static final String RANGE_ELECTRIC = "electric";
- public static final String RANGE_ELECTRIC_MAX = "electric-max";
- public static final String SOC = "soc";
- public static final String SOC_MAX = "soc-max";
- public static final String RANGE_FUEL = "fuel";
- public static final String REMAINING_FUEL = "remaining-fuel";
- public static final String RANGE_RADIUS_ELECTRIC = "radius-electric";
- public static final String RANGE_RADIUS_ELECTRIC_MAX = "radius-electric-max";
- public static final String RANGE_RADIUS_FUEL = "radius-fuel";
- public static final String RANGE_RADIUS_HYBRID = "radius-hybrid";
- public static final String RANGE_RADIUS_HYBRID_MAX = "radius-hybrid-max";
-
- // Last Trip
- public static final String DURATION = "duration";
- public static final String DISTANCE = "distance";
- public static final String DISTANCE_SINCE_CHARGING = "distance-since-charging";
- public static final String AVG_CONSUMPTION = "avg-consumption";
- public static final String AVG_COMBINED_CONSUMPTION = "avg-combined-consumption";
- public static final String AVG_RECUPERATION = "avg-recuperation";
-
- // Lifetime + Average Consumptions
- public static final String TOTAL_DRIVEN_DISTANCE = "total-driven-distance";
- public static final String SINGLE_LONGEST_DISTANCE = "single-longest-distance";
-
- // Image
- public static final String IMAGE_FORMAT = "png";
- public static final String IMAGE_VIEWPORT = "view";
- public static final String IMAGE_SIZE = "size";
-
- // Remote Services
- public static final String REMOTE_SERVICE_LIGHT_FLASH = "light";
- public static final String REMOTE_SERVICE_VEHICLE_FINDER = "finder";
- public static final String REMOTE_SERVICE_DOOR_LOCK = "lock";
- public static final String REMOTE_SERVICE_DOOR_UNLOCK = "unlock";
- public static final String REMOTE_SERVICE_HORN = "horn";
- public static final String REMOTE_SERVICE_AIR_CONDITIONING = "climate";
- public static final String REMOTE_SERVICE_CHARGE_NOW = "charge-now";
- public static final String REMOTE_SERVICE_CHARGING_CONTROL = "charge-control";
- public static final String REMOTE_SERVICE_COMMAND = "command";
- public static final String REMOTE_STATE = "state";
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.*;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.BMWConnectedDriveOptionProvider;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.ConnectedDriveBridgeHandler;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.VehicleHandler;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.core.i18n.LocaleProvider;
-import org.openhab.core.i18n.TimeZoneProvider;
-import org.openhab.core.io.net.http.HttpClientFactory;
-import org.openhab.core.thing.Bridge;
-import org.openhab.core.thing.Thing;
-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.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-
-/**
- * The {@link ConnectedDriveHandlerFactory} is responsible for creating things and thing
- * handlers.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@Component(configurationPid = "binding.bmwconnecteddrive", service = ThingHandlerFactory.class)
-public class ConnectedDriveHandlerFactory extends BaseThingHandlerFactory {
-
- private final HttpClientFactory httpClientFactory;
- private final BMWConnectedDriveOptionProvider optionProvider;
- private boolean imperial = false;
-
- @Activate
- public ConnectedDriveHandlerFactory(final @Reference HttpClientFactory hcf,
- final @Reference BMWConnectedDriveOptionProvider op, final @Reference LocaleProvider lp,
- final @Reference TimeZoneProvider timeZoneProvider) {
- httpClientFactory = hcf;
- optionProvider = op;
- imperial = IMPERIAL_COUNTRIES.contains(lp.getLocale().getCountry());
- Converter.setTimeZoneProvider(timeZoneProvider);
- }
-
- @Override
- public boolean supportsThingType(ThingTypeUID thingTypeUID) {
- return SUPPORTED_THING_SET.contains(thingTypeUID);
- }
-
- @Override
- protected @Nullable ThingHandler createHandler(Thing thing) {
- ThingTypeUID thingTypeUID = thing.getThingTypeUID();
- if (THING_TYPE_CONNECTED_DRIVE_ACCOUNT.equals(thingTypeUID)) {
- return new ConnectedDriveBridgeHandler((Bridge) thing, httpClientFactory);
- } else if (SUPPORTED_THING_SET.contains(thingTypeUID)) {
- VehicleHandler vh = new VehicleHandler(thing, optionProvider, thingTypeUID.getId(), imperial);
- return vh;
- }
- return null;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-
-/**
- * The {@link VehicleConfiguration} class contains fields mapping thing configuration parameters.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class VehicleConfiguration {
- /**
- * Vehicle Identification Number (VIN)
- */
- public String vin = Constants.EMPTY;
-
- /**
- * Data refresh rate in minutes
- */
- public int refreshInterval = ConnectedDriveConstants.DEFAULT_REFRESH_INTERVAL_MINUTES;
-
- /**
- * Either Auto Detect Miles units (UK & US) or select Format directly
- * <option value="AUTODETECT">Auto Detect</option>
- * <option value="METRIC">Metric</option>
- * <option value="IMPERIAL">Imperial</option>
- */
- public String units = ConnectedDriveConstants.UNITS_AUTODETECT;
-
- /**
- * image size - width & length (square)
- */
- public int imageSize = ConnectedDriveConstants.DEFAULT_IMAGE_SIZE_PX;
-
- /**
- * image viewport defined as options in thing xml
- * <option value="FRONT">Front</option>
- * <option value="REAR">Rear</option>
- * <option value="SIDE">Slide</option>
- * <option value="DASHBOARD">Dashboard</option>
- * <option value="DRIVERDOOR">Driver Door</option>
- */
- public String imageViewport = ConnectedDriveConstants.DEFAULT_IMAGE_VIEWPORT;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.action;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileWrapper.ProfileKey.*;
-
-import java.time.DayOfWeek;
-import java.time.LocalTime;
-import java.util.Optional;
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.VehicleHandler;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileWrapper;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileWrapper.ProfileKey;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.ActionOutput;
-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;
-
-/**
- * The {@link BMWConnectedDriveActions} provides actions for VehicleHandler
- *
- * @author Norbert Truchsess - Initial contribution
- */
-@ThingActionsScope(name = "bmwconnecteddrive")
-@NonNullByDefault
-public class BMWConnectedDriveActions implements ThingActions {
-
- private Optional<VehicleHandler> handler = Optional.empty();
-
- private Optional<ChargeProfileWrapper> profile = Optional.empty();
-
- @RuleAction(label = "getTimer1Departure", description = "returns the departure time of timer1")
- public @ActionOutput(name = "time", type = "java.util.Optional<java.time.LocalTime>") Optional<LocalTime> getTimer1Departure() {
- return getTime(TIMER1);
- }
-
- @RuleAction(label = "setTimer1Departure", description = "sets the timer1 departure time")
- public void setTimer1Departure(@ActionInput(name = "time", type = "java.time.LocalTime") @Nullable LocalTime time) {
- setTime(TIMER1, time);
- }
-
- @RuleAction(label = "getTimer1Enabled", description = "returns the enabled state of timer1")
- public @ActionOutput(name = "enabled", type = "java.util.Optional<java.lang.Boolean>") Optional<Boolean> getTimer1Enabled() {
- return getEnabled(TIMER1);
- }
-
- @RuleAction(label = "setTimer1Enabled", description = "sets the enabled state of timer1")
- public void setTimer1Enabled(@ActionInput(name = "enabled", type = "java.lang.Boolean") @Nullable Boolean enabled) {
- setEnabled(TIMER1, enabled);
- }
-
- @RuleAction(label = "getTimer2Departure", description = "returns the departure time of timer2")
- public @ActionOutput(name = "time", type = "java.util.Optional<java.time.LocalTime>") Optional<LocalTime> getTimer2Departure() {
- return getTime(TIMER2);
- }
-
- @RuleAction(label = "setTimer2Departure", description = "sets the timer2 departure time")
- public void setTimer2Departure(@ActionInput(name = "time", type = "java.time.LocalTime") @Nullable LocalTime time) {
- setTime(TIMER2, time);
- }
-
- @RuleAction(label = "getTimer2Enabled", description = "returns the enabled state of timer2")
- public @ActionOutput(name = "enabled", type = "java.util.Optional<java.lang.Boolean>") Optional<Boolean> getTimer2Enabled() {
- return getEnabled(TIMER2);
- }
-
- @RuleAction(label = "setTimer2Enabled", description = "sets the enabled state of timer2")
- public void setTimer2Enabled(@ActionInput(name = "enabled", type = "java.lang.Boolean") @Nullable Boolean enabled) {
- setEnabled(TIMER2, enabled);
- }
-
- @RuleAction(label = "getTimer3Departure", description = "returns the departure time of timer3")
- public @ActionOutput(name = "time", type = "java.util.Optional<java.time.LocalTime>") Optional<LocalTime> getTimer3Departure() {
- return getTime(TIMER3);
- }
-
- @RuleAction(label = "setTimer3Departure", description = "sets the timer3 departure time")
- public void setTimer3Departure(@ActionInput(name = "time", type = "java.time.LocalTime") @Nullable LocalTime time) {
- setTime(TIMER3, time);
- }
-
- @RuleAction(label = "getTimer3Enabled", description = "returns the enabled state of timer3")
- public @ActionOutput(name = "enabled", type = "java.util.Optional<java.lang.Boolean>") Optional<Boolean> getTimer3Enabled() {
- return getEnabled(TIMER3);
- }
-
- @RuleAction(label = "setTimer3Enabled", description = "sets the enabled state of timer3")
- public void setTimer3Enabled(@ActionInput(name = "enabled", type = "java.lang.Boolean") @Nullable Boolean enabled) {
- setEnabled(TIMER3, enabled);
- }
-
- @RuleAction(label = "getOverrideTimerDeparture", description = "returns the departure time of overrideTimer")
- public @ActionOutput(name = "time", type = "java.util.Optional<java.time.LocalTime>") Optional<LocalTime> getOverrideTimerDeparture() {
- return getTime(OVERRIDE);
- }
-
- @RuleAction(label = "setOverrideTimerDeparture", description = "sets the overrideTimer departure time")
- public void setOverrideTimerDeparture(
- @ActionInput(name = "time", type = "java.time.LocalTime") @Nullable LocalTime time) {
- setTime(OVERRIDE, time);
- }
-
- @RuleAction(label = "getOverrideTimerEnabled", description = "returns the enabled state of overrideTimer")
- public @ActionOutput(name = "enabled", type = "java.util.Optional<java.lang.Boolean>") Optional<Boolean> getOverrideTimerEnabled() {
- return getEnabled(OVERRIDE);
- }
-
- @RuleAction(label = "setOverrideTimerEnabled", description = "sets the enabled state of overrideTimer")
- public void setOverrideTimerEnabled(
- @ActionInput(name = "enabled", type = "java.lang.Boolean") @Nullable Boolean enabled) {
- setEnabled(OVERRIDE, enabled);
- }
-
- @RuleAction(label = "getPreferredWindowStart", description = "returns the preferred charging-window start time")
- public @ActionOutput(name = "time", type = "java.util.Optional<java.time.LocalTime>") Optional<LocalTime> getPreferredWindowStart() {
- return getTime(WINDOWSTART);
- }
-
- @RuleAction(label = "setPreferredWindowStart", description = "sets the preferred charging-window start time")
- public void setPreferredWindowStart(
- @ActionInput(name = "time", type = "java.time.LocalTime") @Nullable LocalTime time) {
- setTime(WINDOWSTART, time);
- }
-
- @RuleAction(label = "getPreferredWindowEnd", description = "returns the preferred charging-window end time")
- public @ActionOutput(name = "time", type = "java.util.Optional<java.time.LocalTime>") Optional<LocalTime> getPreferredWindowEnd() {
- return getTime(WINDOWEND);
- }
-
- @RuleAction(label = "setPreferredWindowEnd", description = "sets the preferred charging-window end time")
- public void setPreferredWindowEnd(
- @ActionInput(name = "time", type = "java.time.LocalTime") @Nullable LocalTime time) {
- setTime(WINDOWEND, time);
- }
-
- @RuleAction(label = "getClimatizationEnabled", description = "returns the enabled state of climatization")
- public @ActionOutput(name = "enabled", type = "java.util.Optional<java.lang.Boolean>") Optional<Boolean> getClimatizationEnabled() {
- return getEnabled(CLIMATE);
- }
-
- @RuleAction(label = "setClimatizationEnabled", description = "sets the enabled state of climatization")
- public void setClimatizationEnabled(
- @ActionInput(name = "enabled", type = "java.lang.Boolean") @Nullable Boolean enabled) {
- setEnabled(CLIMATE, enabled);
- }
-
- @RuleAction(label = "getChargingMode", description = "gets the charging-mode")
- public @ActionOutput(name = "mode", type = "java.util.Optional<java.lang.String>") Optional<String> getChargingMode() {
- return getProfile().map(profile -> profile.getMode());
- }
-
- @RuleAction(label = "setChargingMode", description = "sets the charging-mode")
- public void setChargingMode(@ActionInput(name = "mode", type = "java.lang.String") @Nullable String mode) {
- getProfile().ifPresent(profile -> profile.setMode(mode));
- }
-
- @RuleAction(label = "getTimer1Days", description = "returns the days of week timer1 is enabled for")
- public @ActionOutput(name = "days", type = "java.util.Optional<java.util.Set<java.time.DayOfWeek>>") Optional<Set<DayOfWeek>> getTimer1Days() {
- return getDays(TIMER1);
- }
-
- @RuleAction(label = "setTimer1Days", description = "sets the days of week timer1 is enabled for")
- public void setTimer1Days(
- @ActionInput(name = "days", type = "java.util.Set<java.time.DayOfWeek>") @Nullable Set<DayOfWeek> days) {
- setDays(TIMER1, days);
- }
-
- @RuleAction(label = "getTimer2Days", description = "returns the days of week timer2 is enabled for")
- public @ActionOutput(name = "days", type = "java.util.Optional<java.util.Set<java.time.DayOfWeek>>") Optional<Set<DayOfWeek>> getTimer2Days() {
- return getDays(TIMER2);
- }
-
- @RuleAction(label = "setTimer2Days", description = "sets the days of week timer2 is enabled for")
- public void setTimer2Days(
- @ActionInput(name = "days", type = "java.util.Set<java.time.DayOfWeek>") @Nullable Set<DayOfWeek> days) {
- setDays(TIMER2, days);
- }
-
- @RuleAction(label = "getTimer3Days", description = "returns the days of week timer3 is enabled for")
- public @ActionOutput(name = "days", type = "java.util.Optional<java.util.Set<java.time.DayOfWeek>>") Optional<Set<DayOfWeek>> getTimer3Days() {
- return getDays(TIMER3);
- }
-
- @RuleAction(label = "setTimer3Days", description = "sets the days of week timer3 is enabled for")
- public void setTimer3Days(
- @ActionInput(name = "days", type = "java.util.Set<java.time.DayOfWeek>") @Nullable Set<DayOfWeek> days) {
- setDays(TIMER3, days);
- }
-
- @RuleAction(label = "getOverrideTimerDays", description = "returns the days of week the overrideTimer is enabled for")
- public @ActionOutput(name = "days", type = "java.util.Optional<java.util.Set<java.time.DayOfWeek>>") Optional<Set<DayOfWeek>> getOverrideTimerDays() {
- return getDays(OVERRIDE);
- }
-
- @RuleAction(label = "setOverrideTimerDays", description = "sets the days of week the overrideTimer is enabled for")
- public void setOverrideTimerDays(
- @ActionInput(name = "days", type = "java.util.Set<java.time.DayOfWeek>") @Nullable Set<DayOfWeek> days) {
- setDays(OVERRIDE, days);
- }
-
- @RuleAction(label = "sendChargeProfile", description = "sends the charging profile to the vehicle")
- public void sendChargeProfile() {
- handler.ifPresent(handle -> handle.sendChargeProfile(getProfile()));
- }
-
- @RuleAction(label = "cancel", description = "cancel current edit of charging profile")
- public void cancelEditChargeProfile() {
- profile = Optional.empty();
- }
-
- public static Optional<LocalTime> getTimer1Departure(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getTimer1Departure();
- }
-
- public static void setTimer1Departure(ThingActions actions, @Nullable LocalTime time) {
- ((BMWConnectedDriveActions) actions).setTimer1Departure(time);
- }
-
- public static Optional<Boolean> getTimer1Enabled(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getTimer1Enabled();
- }
-
- public static void setTimer1Enabled(ThingActions actions, @Nullable Boolean enabled) {
- ((BMWConnectedDriveActions) actions).setTimer1Enabled(enabled);
- }
-
- public static Optional<LocalTime> getTimer2Departure(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getTimer2Departure();
- }
-
- public static void setTimer2Departure(ThingActions actions, @Nullable LocalTime time) {
- ((BMWConnectedDriveActions) actions).setTimer2Departure(time);
- }
-
- public static Optional<Boolean> getTimer2Enabled(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getTimer2Enabled();
- }
-
- public static void setTimer2Enabled(ThingActions actions, @Nullable Boolean enabled) {
- ((BMWConnectedDriveActions) actions).setTimer2Enabled(enabled);
- }
-
- public static Optional<LocalTime> getTimer3Departure(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getTimer3Departure();
- }
-
- public static void setTimer3Departure(ThingActions actions, @Nullable LocalTime time) {
- ((BMWConnectedDriveActions) actions).setTimer3Departure(time);
- }
-
- public static Optional<Boolean> getTimer3Enabled(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getTimer3Enabled();
- }
-
- public static void setTimer3Enabled(ThingActions actions, @Nullable Boolean enabled) {
- ((BMWConnectedDriveActions) actions).setTimer3Enabled(enabled);
- }
-
- public static Optional<LocalTime> getOverrideTimerDeparture(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getOverrideTimerDeparture();
- }
-
- public static void setOverrideTimerDeparture(ThingActions actions, @Nullable LocalTime time) {
- ((BMWConnectedDriveActions) actions).setOverrideTimerDeparture(time);
- }
-
- public static Optional<Boolean> getOverrideTimerEnabled(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getOverrideTimerEnabled();
- }
-
- public static void setOverrideTimerEnabled(ThingActions actions, @Nullable Boolean enabled) {
- ((BMWConnectedDriveActions) actions).setOverrideTimerEnabled(enabled);
- }
-
- public static Optional<LocalTime> getPreferredWindowStart(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getPreferredWindowStart();
- }
-
- public static void setPreferredWindowStart(ThingActions actions, @Nullable LocalTime time) {
- ((BMWConnectedDriveActions) actions).setPreferredWindowStart(time);
- }
-
- public static Optional<LocalTime> getPreferredWindowEnd(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getPreferredWindowEnd();
- }
-
- public static void setPreferredWindowEnd(ThingActions actions, @Nullable LocalTime time) {
- ((BMWConnectedDriveActions) actions).setPreferredWindowEnd(time);
- }
-
- public static Optional<Boolean> getClimatizationEnabled(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getClimatizationEnabled();
- }
-
- public static void setClimatizationEnabled(ThingActions actions, @Nullable Boolean enabled) {
- ((BMWConnectedDriveActions) actions).setClimatizationEnabled(enabled);
- }
-
- public static Optional<String> getChargingMode(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getChargingMode();
- }
-
- public static void setChargingMode(ThingActions actions, @Nullable String mode) {
- ((BMWConnectedDriveActions) actions).setChargingMode(mode);
- }
-
- public static Optional<Set<DayOfWeek>> getTimer1Days(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getTimer1Days();
- }
-
- public static void setTimer1Days(ThingActions actions, @Nullable Set<DayOfWeek> days) {
- ((BMWConnectedDriveActions) actions).setTimer1Days(days);
- }
-
- public static Optional<Set<DayOfWeek>> getTimer2Days(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getTimer2Days();
- }
-
- public static void setTimer2Days(ThingActions actions, @Nullable Set<DayOfWeek> days) {
- ((BMWConnectedDriveActions) actions).setTimer2Days(days);
- }
-
- public static Optional<Set<DayOfWeek>> getTimer3Days(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getTimer3Days();
- }
-
- public static void setTimer3Days(ThingActions actions, @Nullable Set<DayOfWeek> days) {
- ((BMWConnectedDriveActions) actions).setTimer3Days(days);
- }
-
- public static Optional<Set<DayOfWeek>> getOverrideTimerDays(ThingActions actions) {
- return ((BMWConnectedDriveActions) actions).getOverrideTimerDays();
- }
-
- public static void setOverrideTimerDays(ThingActions actions, @Nullable Set<DayOfWeek> days) {
- ((BMWConnectedDriveActions) actions).setOverrideTimerDays(days);
- }
-
- public static void sendChargeProfile(ThingActions actions) {
- ((BMWConnectedDriveActions) actions).sendChargeProfile();
- }
-
- public static void cancelEditChargeProfile(ThingActions actions) {
- ((BMWConnectedDriveActions) actions).cancelEditChargeProfile();
- }
-
- @Override
- public void setThingHandler(@Nullable ThingHandler handler) {
- if (handler instanceof VehicleHandler) {
- this.handler = Optional.of((VehicleHandler) handler);
- }
- }
-
- @Override
- public @Nullable ThingHandler getThingHandler() {
- return handler.get();
- }
-
- private Optional<ChargeProfileWrapper> getProfile() {
- if (profile.isEmpty()) {
- profile = handler.flatMap(handle -> handle.getChargeProfileWrapper());
- }
- return profile;
- }
-
- private Optional<LocalTime> getTime(ProfileKey key) {
- return getProfile().map(profile -> profile.getTime(key));
- }
-
- private void setTime(ProfileKey key, @Nullable LocalTime time) {
- getProfile().ifPresent(profile -> profile.setTime(key, time));
- }
-
- private Optional<Boolean> getEnabled(ProfileKey key) {
- return getProfile().map(profile -> profile.isEnabled(key));
- }
-
- private void setEnabled(ProfileKey key, @Nullable Boolean enabled) {
- getProfile().ifPresent(profile -> profile.setEnabled(key, enabled));
- }
-
- private Optional<Set<DayOfWeek>> getDays(ProfileKey key) {
- return getProfile().map(profile -> profile.getDays(key));
- }
-
- private void setDays(ProfileKey key, @Nullable Set<DayOfWeek> days) {
- getProfile().ifPresent(profile -> {
- profile.setDays(key, days);
- });
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.discovery;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.SUPPORTED_THING_SET;
-
-import java.lang.reflect.Field;
-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.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.discovery.VehiclesContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.ConnectedDriveBridgeHandler;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.core.config.core.Configuration;
-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.ThingUID;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link VehicleDiscovery} requests data from ConnectedDrive and is identifying the Vehicles after response
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class VehicleDiscovery extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService {
-
- private final Logger logger = LoggerFactory.getLogger(VehicleDiscovery.class);
- private static final int DISCOVERY_TIMEOUT = 10;
- private Optional<ConnectedDriveBridgeHandler> bridgeHandler = Optional.empty();
-
- public VehicleDiscovery() {
- super(SUPPORTED_THING_SET, DISCOVERY_TIMEOUT, false);
- }
-
- public void onResponse(VehiclesContainer container) {
- bridgeHandler.ifPresent(bridge -> {
- final ThingUID bridgeUID = bridge.getThing().getUID();
- container.vehicles.forEach(vehicle -> {
- // the DriveTrain field in the delivered json is defining the Vehicle Type
- String vehicleType = vehicle.driveTrain.toLowerCase();
- SUPPORTED_THING_SET.forEach(entry -> {
- if (entry.getId().equals(vehicleType)) {
- ThingUID uid = new ThingUID(entry, vehicle.vin, bridgeUID.getId());
- Map<String, String> properties = new HashMap<>();
- // Dealer
- if (vehicle.dealer != null) {
- properties.put("dealer", vehicle.dealer.name);
- properties.put("dealerAddress", vehicle.dealer.street + " " + vehicle.dealer.country + " "
- + vehicle.dealer.postalCode + " " + vehicle.dealer.city);
- properties.put("dealerPhone", vehicle.dealer.phone);
- }
-
- // Services & Support
- properties.put("servicesActivated", getObject(vehicle, Constants.ACTIVATED));
- String servicesSupported = getObject(vehicle, Constants.SUPPORTED);
- String servicesNotSupported = getObject(vehicle, Constants.NOT_SUPPORTED);
- if (vehicle.statisticsAvailable) {
- servicesSupported += Constants.STATISTICS;
- } else {
- servicesNotSupported += Constants.STATISTICS;
- }
- properties.put(Constants.SERVICES_SUPPORTED, servicesSupported);
- properties.put("servicesNotSupported", servicesNotSupported);
- properties.put("supportBreakdownNumber", vehicle.breakdownNumber);
-
- // Vehicle Properties
- if (vehicle.supportedChargingModes != null) {
- properties.put("vehicleChargeModes",
- String.join(Constants.SPACE, vehicle.supportedChargingModes));
- }
- if (vehicle.hasAlarmSystem) {
- properties.put("vehicleAlarmSystem", "Available");
- } else {
- properties.put("vehicleAlarmSystem", "Not Available");
- }
- properties.put("vehicleBrand", vehicle.brand);
- properties.put("vehicleBodytype", vehicle.bodytype);
- properties.put("vehicleColor", vehicle.color);
- properties.put("vehicleConstructionYear", Short.toString(vehicle.yearOfConstruction));
- properties.put("vehicleDriveTrain", vehicle.driveTrain);
- properties.put("vehicleModel", vehicle.model);
- if (vehicle.chargingControl != null) {
- properties.put("vehicleChargeControl", Converter.toTitleCase(vehicle.model));
- }
-
- // Update Properties for already created Things
- bridge.getThing().getThings().forEach(vehicleThing -> {
- Configuration c = vehicleThing.getConfiguration();
- if (c.containsKey(ConnectedDriveConstants.VIN)) {
- String thingVIN = c.get(ConnectedDriveConstants.VIN).toString();
- if (vehicle.vin.equals(thingVIN)) {
- vehicleThing.setProperties(properties);
- }
- }
- });
-
- // Properties needed for functional THing
- properties.put(ConnectedDriveConstants.VIN, vehicle.vin);
- properties.put("refreshInterval",
- Integer.toString(ConnectedDriveConstants.DEFAULT_REFRESH_INTERVAL_MINUTES));
- properties.put("units", ConnectedDriveConstants.UNITS_AUTODETECT);
- properties.put("imageSize", Integer.toString(ConnectedDriveConstants.DEFAULT_IMAGE_SIZE_PX));
- properties.put("imageViewport", ConnectedDriveConstants.DEFAULT_IMAGE_VIEWPORT);
-
- String vehicleLabel = vehicle.brand + " " + vehicle.model;
- Map<String, Object> convertedProperties = new HashMap<String, Object>(properties);
- thingDiscovered(DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
- .withRepresentationProperty(ConnectedDriveConstants.VIN).withLabel(vehicleLabel)
- .withProperties(convertedProperties).build());
- }
- });
- });
- });
- };
-
- /**
- * Get all field names from a DTO with a specific value
- * Used to get e.g. all services which are "ACTIVATED"
- *
- * @param DTO Object
- * @param compare String which needs to map with the value
- * @return String with all field names matching this value separated with Spaces
- */
- public String getObject(Object dto, String compare) {
- StringBuilder buf = new StringBuilder();
- for (Field field : dto.getClass().getDeclaredFields()) {
- try {
- Object value = field.get(dto);
- if (compare.equals(value)) {
- buf.append(Converter.capitalizeFirst(field.getName()) + Constants.SPACE);
- }
- } catch (IllegalArgumentException | IllegalAccessException e) {
- logger.debug("Field {} not found {}", compare, e.getMessage());
- }
- }
- return buf.toString();
- }
-
- @Override
- public void setThingHandler(ThingHandler handler) {
- if (handler instanceof ConnectedDriveBridgeHandler) {
- bridgeHandler = Optional.of((ConnectedDriveBridgeHandler) handler);
- bridgeHandler.get().setDiscoveryService(this);
- }
- }
-
- @Override
- public @Nullable ThingHandler getThingHandler() {
- return bridgeHandler.orElse(null);
- }
-
- @Override
- protected void startScan() {
- bridgeHandler.ifPresent(ConnectedDriveBridgeHandler::requestVehicles);
- }
-
- @Override
- public void deactivate() {
- super.deactivate();
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.utils.Constants.*;
-
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-
-/**
- * The {@link Destination} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class Destination {
- public float lat;
- public float lon;
- public String country;
- public String city;
- public String street;
- public String streetNumber;
- public String type;
- public String createdAt;
-
- public String getAddress() {
- StringBuilder buf = new StringBuilder();
- if (street != null) {
- buf.append(street);
- if (streetNumber != null) {
- buf.append(SPACE).append(streetNumber);
- }
- }
- if (city != null) {
- if (buf.length() > 0) {
- buf.append(COMMA).append(SPACE).append(city);
- } else {
- buf.append(city);
- }
- }
- if (buf.length() == 0) {
- return UNDEF;
- } else {
- return Converter.toTitleCase(buf.toString());
- }
- }
-
- public String getCoordinates() {
- return lat + Constants.COMMA + lon;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import java.util.List;
-
-/**
- * The {@link DestinationContainer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class DestinationContainer {
- public List<Destination> destinations;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-
-/**
- * The {@link NetworkError} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class NetworkError {
- public String url;
- public int status;
- public String reason;
- public String params;
-
- @Override
- public String toString() {
- return new StringBuilder(url).append(Constants.HYPHEN).append(status).append(Constants.HYPHEN).append(reason)
- .append(params).toString();
- }
-
- public String toJson() {
- return Converter.getGson().toJson(this);
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.auth;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link Timer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class AuthResponse {
- @SerializedName("access_token")
- public String accessToken;
- @SerializedName("token_type")
- public String tokenType;
- @SerializedName("expires_in")
- public int expiresIn;
-
- @Override
- public String toString() {
- return "Token " + accessToken + " type " + tokenType + " expires in " + expiresIn;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.charge;
-
-/**
- * The {@link ChargeProfile} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- * @author Norbert Truchsess - edit & send of charge profile
- */
-public class ChargeProfile {
- public WeeklyPlanner weeklyPlanner;
- public WeeklyPlanner twoTimesTimer;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.charge;
-
-/**
- * The {@link ChargingWindow} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class ChargingWindow {
- public String startTime;// ":"11:00",
- public String endTime;// ":"17:00"}}
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.charge;
-
-import java.util.List;
-
-/**
- * The {@link Timer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- * @author Norbert Truchsess - edit & send of charge profile
- */
-public class Timer {
- public String departureTime;// ": "05:00",
- public Boolean timerEnabled;// ": false,
- public List<String> weekdays;
- /**
- * "MONDAY",
- * "TUESDAY",
- * "WEDNESDAY",
- * "THURSDAY",
- * "FRIDAY"
- * ] '
- */
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.charge;
-
-/**
- * The {@link WeeklyPlanner} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- * @author Norbert Truchsess - edit & send of charge profile
- */
-public class WeeklyPlanner {
- public Boolean climatizationEnabled; // ": true,
- public String chargingMode;// ": "IMMEDIATE_CHARGING",
- public String chargingPreferences; // ": "CHARGING_WINDOW",
- public Timer timer1; // : {
- public Timer timer2;// ": {
- public Timer timer3;// ":{"departureTime":"00:00","timerEnabled":false,"weekdays":[]},"
- public Timer overrideTimer;// ":{"departureTime":"12:00","timerEnabled":false,"weekdays":["SATURDAY"]},"
- public ChargingWindow preferredChargingWindow;// ":{"startTime":"11:00","endTime":"17:00"}}
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.compat;
-
-/**
- * The {@link CBSMessageCompat} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class CBSMessageCompat {
- public String description; // "Nächster Wechsel spätestens zum angegebenen Termin.",
- public String text; // "Bremsflüssigkeit",
- public int id; // 3,
- public String status; // "OK",
- public String messageType; // "CBS",
- public String date; // "2021-11"
- public int unitOfLengthRemaining; // ": "2000"
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.compat;
-
-/**
- * The {@link CCMMessageCompat} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class CCMMessageCompat {
- public String text;// "Laden nicht möglich"
- public int id;// 804,
- public String status;// "NULL",
- public String messageType;// "CCM",
- public int unitOfLengthRemaining = -1; // "18312"
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.compat;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link VehicleAttributes} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class VehicleAttributes {
- // Windows & Doors
- @SerializedName("door_driver_front")
- public String doorDriverFront;// "CLOSED",
- @SerializedName("door_driver_rear")
- public String doorDriverRear;// "CLOSED",
- @SerializedName("door_passenger_front")
- public String doorPassengerFront;// "CLOSED",
- @SerializedName("door_passenger_rear")
- public String doorPassengerRear;// "CLOSED",
- @SerializedName("hood_state")
- public String hoodState;// "CLOSED",
- @SerializedName("trunk_state")
- public String trunkState;// "CLOSED",
- @SerializedName("window_driver_front")
- public String winDriverFront;// "CLOSED",
- @SerializedName("window_driver_rear")
- public String winDriverRear;// "CLOSED",
- @SerializedName("window_passenger_front")
- public String winPassengerFront;// "CLOSED",
- @SerializedName("window_passenger_rear")
- public String winPassengerRear;// "CLOSED",
- @SerializedName("sunroof_state")
- public String sunroofState;// "CLOSED",
- @SerializedName("door_lock_state")
- public String doorLockState;// "SECURED",
- public String shdStatusUnified;// "CLOSED",
-
- // Charge Status
- public String chargingHVStatus;// "INVALID",
- public String lastChargingEndReason;// "CHARGING_GOAL_REACHED",
- public String connectorStatus;// "DISCONNECTED",
- public String chargingLogicCurrentlyActive;// "NOT_CHARGING",
- public String chargeNowAllowed;// "NOT_ALLOWED",
- @SerializedName("charging_status")
- public String chargingStatus;// "NOCHARGING",
- public String lastChargingEndResult;// "SUCCESS",
- public String chargingSystemStatus;// "NOCHARGING",
- public String lastUpdateReason;// "VEHCSHUTDOWN_SECURED"
-
- // Range
- public int mileage;// "17236",
- public double beMaxRangeElectric;// "209.0",
- public double beMaxRangeElectricKm;// "209.0",
- public double beRemainingRangeElectric;// "179.0",
- public double beRemainingRangeElectricKm;// "179.0",
- public double beMaxRangeElectricMile;// "129.0",
- public double beRemainingRangeElectricMile;// "111.0",
- public double beRemainingRangeFuelKm;// "67.0",
- public double beRemainingRangeFuelMile;// "41.0",
- public double beRemainingRangeFuel;// "67.0",
- @SerializedName("kombi_current_remaining_range_fuel")
- public double kombiRemainingRangeFuel;// "67.0",
-
- public double chargingLevelHv;// "89.0",
- @SerializedName("soc_hv_percent")
- public double socHvPercent;// "82.6",
- @SerializedName("remaining_fuel")
- public double remainingFuel;// "4",
- public double fuelPercent;// "47",
-
- // Last Status update
- public String updateTime;// "22.08.2020 12:55:46 UTC",
- @SerializedName("updateTime_converted")
- public String updateTimeConverted;// "22.08.2020 13:55",
- @SerializedName("updateTime_converted_date")
- public String updateTimeConvertedDate;// "22.08.2020",
- @SerializedName("updateTime_converted_time")
- public String updateTimeConvertedTime;// "13:55",
- @SerializedName("updateTime_converted_timestamp")
- public String updateTimeConvertedTimestamp;// "1598104546000",
-
- // Last Trip Update
- @SerializedName("Segment_LastTrip_time_segment_end")
- public String lastTripEnd;// "22.08.2020 14:52:00 UTC",
- @SerializedName("Segment_LastTrip_time_segment_end_formatted")
- public String lastTripEndFormatted;// "22.08.2020 14:52",
- @SerializedName("Segment_LastTrip_time_segment_end_formatted_date")
- public String lastTripEndFormattedDate;// "22.08.2020",
- @SerializedName("Segment_LastTrip_time_segment_end_formatted_time")
- public String lastTripEndFormattedTime;// "14:52",
-
- // Location
- @SerializedName("gps_lat")
- public float gpsLat;// "43.21",
- @SerializedName("gps_lng")
- public float gpsLon;// "8.765",
- public int heading;// "41",
-
- public String unitOfLength;// "km",
- public String unitOfEnergy;// "kWh",
- @SerializedName("vehicle_tracking")
- public String vehicleTracking;// "1",
- @SerializedName("head_unit_pu_software")
- public String headunitSoftware;// "07/16",
- @SerializedName("check_control_messages")
- public String checkControlMessages;// "",
- @SerializedName("sunroof_position")
- public String sunroofPosition;// "0",
- @SerializedName("single_immediate_charging")
- public String singleImmediateCharging;// "isUnused",
- public String unitOfCombustionConsumption;// "l/100km",
- @SerializedName("Segment_LastTrip_ratio_electric_driven_distance")
- public String lastTripElectricRation;// "100",
- @SerializedName("condition_based_services")
- public String conditionBasedServices;// "00003,OK,2021-11,;00017,OK,2021-11,;00001,OK,2021-11,;00032,OK,2021-11,",
- @SerializedName("charging_inductive_positioning")
- public String chargingInductivePositioning;// "not_positioned",
- @SerializedName("lsc_trigger")
- public String lscTrigger;// "VEHCSHUTDOWN_SECURED",
- @SerializedName("lights_parking")
- public String lightsParking;// "OFF",
- public String prognosisWhileChargingStatus;// "NOT_NEEDED",
- @SerializedName("head_unit")
- public String headunit;// "EntryNav",
- @SerializedName("battery_size_max")
- public String batterySizeMax;// "33200",
- @SerializedName("charging_connection_type")
- public String chargingConnectionType;// "CONDUCTIVE",
- public String unitOfElectricConsumption;// "kWh/100km",
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.compat;
-
-/**
- * The {@link VehicleAttributesContainer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class VehicleAttributesContainer {
- public VehicleAttributes attributesMap;
- public VehicleMessages vehicleMessages;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.compat;
-
-import java.util.List;
-
-/**
- * The {@link VehicleMessages} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- * @param <CBSMessage>
- */
-public class VehicleMessages {
- public List<CCMMessageCompat> ccmMessages;
- public List<CBSMessageCompat> cbsMessages;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.discovery;
-
-/**
- * The {@link Dealer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class Dealer {
- public String name;
- public String street;
- public String postalCode;
- public String city;
- public String country;
- public String phone;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.discovery;
-
-import java.util.List;
-
-/**
- * The {@link Vehicle} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class Vehicle {
- public String vin;
- public String model;
- public String driveTrain;
- public String brand;
- public short yearOfConstruction;
- public String bodytype;
- public String color;
- public boolean statisticsCommunityEnabled;
- public boolean statisticsAvailable;
- public boolean hasAlarmSystem;
- public Dealer dealer;
- public String breakdownNumber;
- public List<String> supportedChargingModes;
- public String chargingControl;// ": "WEEKLY_PLANNER",
-
- // Remote Services
- public String vehicleFinder; // ACTIVATED
- public String hornBlow; // ACTIVATED
- public String lightFlash; // ACTIVATED
- public String doorLock; // ACTIVATED
- public String doorUnlock; // ACTIVATED
- public String climateNow; // ACTIVATED
- public String sendPoi; // ACTIVATED
-
- public String remote360; // SUPPORTED
- public String climateControl; // SUPPORTED
- public String chargeNow; // SUPPORTED
- public String lastDestinations; // SUPPORTED
- public String carCloud; // SUPPORTED
- public String remoteSoftwareUpgrade; // SUPPORTED
-
- public String climateNowRES;// ": "NOT_SUPPORTED",
- public String climateControlRES;// ": "NOT_SUPPORTED",
- public String smartSolution;// ": "NOT_SUPPORTED",
- public String ipa;// ": "NOT_SUPPORTED",
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.discovery;
-
-import java.util.List;
-
-/**
- * The {@link VehiclesContainer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class VehiclesContainer {
- public List<Vehicle> vehicles;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.navigation;
-
-/**
- * The {@link NavigationContainer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class NavigationContainer {
- // "latitude": 56.789,
- // "longitude": 8.765,
- // "isoCountryCode": "DEU",
- // "auxPowerRegular": 1.4,
- // "auxPowerEcoPro": 1.2,
- // "auxPowerEcoProPlus": 0.4,
- // "soc": 25.952999114990234,
- // "pendingUpdate": false,
- // "vehicleTracking": true,
- public double socmax;// ": 29.84
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.remote;
-
-/**
- * The {@link ExecutionStatus} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class ExecutionStatus {
- public String serviceType;// ": "DOOR_UNLOCK",
- public String status;// ": "EXECUTED",
- public String eventId;// ": "5639303536333926DA7B9400@bmw.de",
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.remote;
-
-/**
- * The {@link ExecutionStatusContainer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class ExecutionStatusContainer {
- public ExecutionStatus executionStatus;
- public String eventId;
- public String creationTime;
- public String eventStatus;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.statistics;
-
-/**
- * The {@link AllTrips} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class AllTrips {
- public CommunityPowerEntry avgElectricConsumption;
- public CommunityPowerEntry avgRecuperation;
- public CommunityChargeCycleEntry chargecycleRange;
- public CommunityEletricDistanceEntry totalElectricDistance;
- public CommunityPowerEntry avgCombinedConsumption;
- public float savedCO2;// ":461.083,"
- public float savedCO2greenEnergy;// ":2712.255,"
- public float totalSavedFuel;// ":0,"
- public String resetDate;// ":"2020-08-24T14:40:40+0000","
- public int batterySizeMax;// ":33200
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.statistics;
-
-/**
- * The {@link AllTripsContainer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class AllTripsContainer {
- public AllTrips allTrips;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.statistics;
-
-/**
- * The {@link CommunityChargeCycleEntry} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class CommunityChargeCycleEntry {
- public float communityAverage;// ": 194.21,
- public float communityHigh;// ": 270,
- public float userAverage;// ": 57.3,
- public float userHigh;// ": 185.48,
- public float userCurrentChargeCycle;// ": 68
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.statistics;
-
-/**
- * The {@link CommunityEletricDistanceEntry} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class CommunityEletricDistanceEntry {
- public float communityLow;// ": 19,
- public float communityAverage;// ": 40850.56,
- public float communityHigh;// ": 193006,
- public float userTotal;// ": 16629.4
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.statistics;
-
-/**
- * The {@link CommunityPowerEntry} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class CommunityPowerEntry {
- public float communityLow;// ": 11.05,
- public float communityAverage;// ": 16.28,
- public float communityHigh;// ": 21.99,
- public float userAverage;// ": 16.46
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.statistics;
-
-/**
- * The {@link LastTrip} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class LastTrip {
- public float efficiencyValue;// ": 0.98,
- public float totalDistance;// ": 2,
- public float electricDistance;// ": 2,
- public float avgElectricConsumption;// ": 7,
- public float avgRecuperation;// ": 6,
- public float drivingModeValue;// ": 0.87,
- public float accelerationValue;// ": 0.99,
- public float anticipationValue;// ": 0.99,
- public float totalConsumptionValue;// ": 1.25,
- public float auxiliaryConsumptionValue;// ": 0.78,
- public float avgCombinedConsumption;// ": 0,
- public float electricDistanceRatio;// ": 100,
- public float savedFuel;// ": 0,
- public String date;// ": "2020-08-24T17:55:00+0000",
- public float duration;// ": 5
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.statistics;
-
-/**
- * The {@link LastTripContainer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class LastTripContainer {
- public LastTrip lastTrip;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.status;
-
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-
-/**
- * The {@link CBSMessage} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class CBSMessage {
- public String cbsType;// ": "BRAKE_FLUID",
- public String cbsState;// ": "OK",
- public String cbsDueDate;// ": "2021-11",
- public String cbsDescription;// ": "Next change due at the latest by the stated date."
- public int cbsRemainingMileage = -1; // 46000
-
- public String cbsTypeConverted = null;
- public String cbsDescriptionConverted = null;
-
- public String getDueDate() {
- if (cbsDueDate == null) {
- return Constants.NULL_DATE;
- } else {
- return cbsDueDate + Constants.UTC_APPENDIX;
- }
- }
-
- public String getType() {
- if (cbsTypeConverted == null) {
- if (cbsType == null) {
- cbsTypeConverted = Constants.INVALID;
- } else {
- cbsTypeConverted = Converter.toTitleCase(cbsType);
- }
- }
- return cbsTypeConverted;
- }
-
- public String getDescription() {
- if (cbsDescriptionConverted == null) {
- if (cbsDescription == null) {
- cbsDescriptionConverted = Constants.INVALID;
- } else {
- cbsDescriptionConverted = cbsDescription;
- }
- }
- return cbsDescriptionConverted;
- }
-
- @Override
- public String toString() {
- return new StringBuilder(cbsDueDate).append(Constants.HYPHEN).append(cbsRemainingMileage)
- .append(Constants.HYPHEN).append(cbsType).toString();
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.status;
-
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-
-/**
- * The {@link CCMMessage} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class CCMMessage {
- // if necessary. Perform reset after adjustment. See Owner's Handbook for further
- // information.",
- public String ccmDescriptionShort = Constants.INVALID;// ": "Tyre pressure notification",
- public String ccmDescriptionLong = Constants.INVALID;// ": "You can continue driving. Check tyre pressure when tyres
- // are cold and adjust
- public int ccmId = -1;// ": 955,
- public int ccmMileage = -1;// ": 41544
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.status;
-
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-
-/**
- * The {@link Doors} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class Doors {
- public String doorDriverFront = Constants.UNDEF;// ": "CLOSED",
- public String doorDriverRear = Constants.UNDEF;// ": "CLOSED",
- public String doorPassengerFront = Constants.UNDEF;// ": "CLOSED",
- public String doorPassengerRear = Constants.UNDEF;// ": "CLOSED",
- public String trunk = Constants.UNDEF;// ": "CLOSED",
- public String hood = Constants.UNDEF;// ": "CLOSED",
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.status;
-
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-
-/**
- * The {@link Position} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class Position {
- public float lat;// ": 46.55605,
- public float lon;// ": 10.495669,
- public int heading;// ": 219,
- public String status;// ": "OK"
-
- public String getCoordinates() {
- return new StringBuilder(Float.toString(lat)).append(Constants.COMMA).append(Float.toString(lon)).toString();
- }
-
- @Override
- public String toString() {
- return getCoordinates();
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.status;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link VehicleStatus} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class VehicleStatus {
- public int mileage = Constants.INT_UNDEF;// ": 17273,
- public double remainingFuel = Constants.INT_UNDEF;// ": 4,
- public double remainingRangeElectric = Constants.INT_UNDEF;// ": 148,
- public double remainingRangeElectricMls;// ": 91,
- public double remainingRangeFuel = Constants.INT_UNDEF;// ": 70,"
- public double remainingRangeFuelMls;// ":43,"
- public double maxRangeElectric = Constants.INT_UNDEF;// ":216,"
- public double maxRangeElectricMls;// ":134,"
- public double maxFuel;// ":8.5,
- public double chargingLevelHv;// ":71,
- public String vin;// : "ANONYMOUS",
- public String updateReason;// ": "VEHICLE_SHUTDOWN_SECURED",
- public String updateTime;// ": "2020-08-24 T15:55:32+0000",
- public String doorDriverFront = Constants.UNDEF;// ": "CLOSED",
- public String doorDriverRear = Constants.UNDEF;// ": "CLOSED",
- public String doorPassengerFront = Constants.UNDEF;// ": "CLOSED",
- public String doorPassengerRear = Constants.UNDEF;// ": "CLOSED",
- public String windowDriverFront = Constants.UNDEF;// ": "CLOSED",
- public String windowDriverRear = Constants.UNDEF;// ": "CLOSED",
- public String windowPassengerFront = Constants.UNDEF;// ": "CLOSED",
- public String windowPassengerRear = Constants.UNDEF;// ": "CLOSED",
- public String sunroof = Constants.UNDEF;// ": "CLOSED",
- public String trunk = Constants.UNDEF;// ": "CLOSED",
- public String rearWindow = Constants.UNDEF;// ": "INVALID",
- public String hood = Constants.UNDEF;// ": "CLOSED",
- public String doorLockState;// ": "SECURED",
- public String parkingLight;// ": "OFF",
- public String positionLight;// ": "ON",
- public String connectionStatus;// ": "DISCONNECTED",
- public String chargingStatus;// ": "INVALID","
- public String lastChargingEndReason;// ": "CHARGING_GOAL_REACHED",
- public String lastChargingEndResult;// ": "SUCCESS","
- public Double chargingTimeRemaining;// ": "45",
- public Position position;
- public String internalDataTimeUTC;// ": "2020-08-24 T15:55:32",
- public boolean singleImmediateCharging;// ":false,
- public String chargingConnectionType;// ": "CONDUCTIVE",
- public String chargingInductivePositioning;// ": "NOT_POSITIONED",
- public String vehicleCountry;// ": "DE","+"
- @SerializedName("DCS_CCH_Activation")
- public String dcsCchActivation;// ": "NA",
- @SerializedName("DCS_CCH_Ongoing")
- public boolean dcsCchOngoing;// ":false
- public List<CCMMessage> checkControlMessages = new ArrayList<CCMMessage>();// ":[],
- public List<CBSMessage> cbsData = new ArrayList<CBSMessage>();
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.status;
-
-/**
- * The {@link VehicleStatusContainer} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class VehicleStatusContainer {
- public VehicleStatus vehicleStatus;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto.status;
-
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-
-/**
- * The {@link Windows} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class Windows {
- public String windowDriverFront = Constants.UNDEF;// ": "CLOSED",
- public String windowDriverRear = Constants.UNDEF;// ": "CLOSED",
- public String windowPassengerFront = Constants.UNDEF;// ": "CLOSED",
- public String windowPassengerRear = Constants.UNDEF;// ": "CLOSED",
- public String sunroof = Constants.UNDEF;// ": "CLOSED",
- public String rearWindow = Constants.UNDEF;// ": "INVALID",
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.events.EventPublisher;
-import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
-import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
-import org.openhab.core.thing.link.ItemChannelLinkRegistry;
-import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-
-/**
- * Dynamic provider of state options while leaving other state description fields as original.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@Component(service = { DynamicStateDescriptionProvider.class, BMWConnectedDriveOptionProvider.class })
-@NonNullByDefault
-public class BMWConnectedDriveOptionProvider extends BaseDynamicStateDescriptionProvider {
-
- @Activate
- public BMWConnectedDriveOptionProvider(final @Reference EventPublisher eventPublisher, //
- final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
- final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
- this.eventPublisher = eventPublisher;
- this.itemChannelLinkRegistry = itemChannelLinkRegistry;
- this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link ByteResponseCallback} Interface for all raw byte results from ASYNC REST API
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public interface ByteResponseCallback extends ResponseCallback {
-
- public void onResponse(byte[] result);
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.utils.Constants.ANONYMOUS;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConfiguration;
-import org.openhab.binding.bmwconnecteddrive.internal.discovery.VehicleDiscovery;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.NetworkError;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.discovery.Dealer;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.discovery.VehiclesContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.BimmerConstants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.core.io.net.http.HttpClientFactory;
-import org.openhab.core.thing.Bridge;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.binding.BaseBridgeHandler;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerService;
-import org.openhab.core.types.Command;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.JsonParseException;
-
-/**
- * The {@link ConnectedDriveBridgeHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class ConnectedDriveBridgeHandler extends BaseBridgeHandler implements StringResponseCallback {
- private final Logger logger = LoggerFactory.getLogger(ConnectedDriveBridgeHandler.class);
- private HttpClientFactory httpClientFactory;
- private Optional<VehicleDiscovery> discoveryService = Optional.empty();
- private Optional<ConnectedDriveProxy> proxy = Optional.empty();
- private Optional<ScheduledFuture<?>> initializerJob = Optional.empty();
- private Optional<String> troubleshootFingerprint = Optional.empty();
-
- public ConnectedDriveBridgeHandler(Bridge bridge, HttpClientFactory hcf) {
- super(bridge);
- httpClientFactory = hcf;
- }
-
- @Override
- public void handleCommand(ChannelUID channelUID, Command command) {
- // no commands available
- }
-
- @Override
- public void initialize() {
- troubleshootFingerprint = Optional.empty();
- updateStatus(ThingStatus.UNKNOWN);
- ConnectedDriveConfiguration config = getConfigAs(ConnectedDriveConfiguration.class);
- logger.debug("Prefer MyBMW API {}", config.preferMyBmw);
- if (!checkConfiguration(config)) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
- } else {
- proxy = Optional.of(new ConnectedDriveProxy(httpClientFactory, config));
- // give the system some time to create all predefined Vehicles
- // check with API call if bridge is online
- initializerJob = Optional.of(scheduler.schedule(this::requestVehicles, 2, TimeUnit.SECONDS));
- Bridge b = super.getThing();
- List<Thing> children = b.getThings();
- logger.debug("Update {} things", children.size());
- children.forEach(entry -> {
- ThingHandler th = entry.getHandler();
- if (th != null) {
- th.dispose();
- th.initialize();
- } else {
- logger.debug("Handler is null");
- }
- });
- }
- }
-
- public static boolean checkConfiguration(ConnectedDriveConfiguration config) {
- if (Constants.EMPTY.equals(config.userName) || Constants.EMPTY.equals(config.password)) {
- return false;
- } else {
- return BimmerConstants.AUTH_SERVER_MAP.containsKey(config.region);
- }
- }
-
- @Override
- public void dispose() {
- initializerJob.ifPresent(job -> job.cancel(true));
- }
-
- public void requestVehicles() {
- proxy.ifPresent(prox -> prox.requestVehicles(this));
- }
-
- // https://www.bmw-connecteddrive.de/api/me/vehicles/v2?all=true&brand=BM
- public String getDiscoveryFingerprint() {
- return troubleshootFingerprint.map(fingerprint -> {
- VehiclesContainer container = null;
- try {
- container = Converter.getGson().fromJson(fingerprint, VehiclesContainer.class);
- if (container != null) {
- if (container.vehicles != null) {
- if (container.vehicles.isEmpty()) {
- return Constants.EMPTY_JSON;
- } else {
- container.vehicles.forEach(entry -> {
- entry.vin = ANONYMOUS;
- entry.breakdownNumber = ANONYMOUS;
- if (entry.dealer != null) {
- Dealer d = entry.dealer;
- d.city = ANONYMOUS;
- d.country = ANONYMOUS;
- d.name = ANONYMOUS;
- d.phone = ANONYMOUS;
- d.postalCode = ANONYMOUS;
- d.street = ANONYMOUS;
- }
- });
- return Converter.getGson().toJson(container);
- }
- } else {
- logger.debug("container.vehicles is null");
- }
- }
- } catch (JsonParseException jpe) {
- logger.debug("Cannot parse fingerprint {}", jpe.getMessage());
- }
- // Not a VehiclesContainer or Vehicles is empty so deliver fingerprint as it is
- return fingerprint;
- }).orElse(Constants.INVALID);
- }
-
- private void logFingerPrint() {
- logger.debug("###### Discovery Troubleshoot Fingerprint Data - BEGIN ######");
- logger.debug("### Discovery Result ###");
- logger.debug("{}", getDiscoveryFingerprint());
- logger.debug("###### Discovery Troubleshoot Fingerprint Data - END ######");
- }
-
- /**
- * There's only the Vehicles response available
- */
- @Override
- public void onResponse(@Nullable String response) {
- boolean firstResponse = troubleshootFingerprint.isEmpty();
- if (response != null) {
- updateStatus(ThingStatus.ONLINE);
- troubleshootFingerprint = discoveryService.map(discovery -> {
- try {
- VehiclesContainer container = Converter.getGson().fromJson(response, VehiclesContainer.class);
- if (container != null) {
- if (container.vehicles != null) {
- discovery.onResponse(container);
- container.vehicles.forEach(entry -> {
- entry.vin = ANONYMOUS;
- entry.breakdownNumber = ANONYMOUS;
- if (entry.dealer != null) {
- Dealer d = entry.dealer;
- d.city = ANONYMOUS;
- d.country = ANONYMOUS;
- d.name = ANONYMOUS;
- d.phone = ANONYMOUS;
- d.postalCode = ANONYMOUS;
- d.street = ANONYMOUS;
- }
- });
- }
- } else {
- troubleshootFingerprint = Optional.of(Constants.EMPTY_JSON);
- }
- } catch (JsonParseException jpe) {
- logger.debug("Fingerprint parse exception {}", jpe.getMessage());
- }
- // Unparseable or not a VehiclesContainer:
- return response;
- });
- } else {
- troubleshootFingerprint = Optional.of(Constants.EMPTY_JSON);
- }
- if (firstResponse) {
- logFingerPrint();
- }
- }
-
- @Override
- public void onError(NetworkError error) {
- boolean firstResponse = troubleshootFingerprint.isEmpty();
- troubleshootFingerprint = Optional.of(error.toJson());
- if (firstResponse) {
- logFingerPrint();
- }
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, error.reason);
- }
-
- @Override
- public Collection<Class<? extends ThingHandlerService>> getServices() {
- return Collections.singleton(VehicleDiscovery.class);
- }
-
- public Optional<ConnectedDriveProxy> getProxy() {
- return proxy;
- }
-
- public void setDiscoveryService(VehicleDiscovery discoveryService) {
- this.discoveryService = Optional.of(discoveryService);
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.utils.HTTPConstants.*;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.Optional;
-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.api.Result;
-import org.eclipse.jetty.client.util.BufferingResponseListener;
-import org.eclipse.jetty.client.util.StringContentProvider;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.util.MultiMap;
-import org.eclipse.jetty.util.UrlEncoded;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConfiguration;
-import org.openhab.binding.bmwconnecteddrive.internal.VehicleConfiguration;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.NetworkError;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.auth.AuthResponse;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.simulation.Injector;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.BimmerConstants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ImageProperties;
-import org.openhab.core.io.net.http.HttpClientFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link ConnectedDriveProxy} This class holds the important constants for the BMW Connected Drive Authorization.
- * They
- * are taken from the Bimmercode from github {@link https://github.com/bimmerconnected/bimmer_connected}
- * File defining these constants
- * {@link https://github.com/bimmerconnected/bimmer_connected/blob/master/bimmer_connected/account.py}
- * https://customer.bmwgroup.com/one/app/oauth.js
- *
- * @author Bernd Weymann - Initial contribution
- * @author Norbert Truchsess - edit & send of charge profile
- */
-@NonNullByDefault
-public class ConnectedDriveProxy {
- private final Logger logger = LoggerFactory.getLogger(ConnectedDriveProxy.class);
- private Optional<RemoteServiceHandler> remoteServiceHandler = Optional.empty();
- private final Token token = new Token();
- private final HttpClient httpClient;
- private final HttpClient authHttpClient;
- private final ConnectedDriveConfiguration configuration;
-
- /**
- * URLs taken from https://github.com/bimmerconnected/bimmer_connected/blob/master/bimmer_connected/const.py
- */
- final String baseUrl;
- final String vehicleUrl;
- final String legacyUrl;
- final String remoteCommandUrl;
- final String remoteStatusUrl;
- final String navigationAPIUrl;
- final String vehicleStatusAPI = "/status";
- final String lastTripAPI = "/statistics/lastTrip";
- final String allTripsAPI = "/statistics/allTrips";
- final String chargeAPI = "/chargingprofile";
- final String destinationAPI = "/destinations";
- final String imageAPI = "/image";
- final String rangeMapAPI = "/rangemap";
- final String serviceExecutionAPI = "/executeService";
- final String serviceExecutionStateAPI = "/serviceExecutionStatus";
- public static final String REMOTE_SERVICE_EADRAX_BASE_URL = "/eadrax-vrccs/v2/presentation/remote-commands/"; // '/{vin}/{service_type}'
- final String remoteServiceEADRXstatusUrl = REMOTE_SERVICE_EADRAX_BASE_URL + "eventStatus?eventId={event_id}";
- final String vehicleEADRXPoiUrl = "/eadrax-dcs/v1/send-to-car/send-to-car";
-
- public ConnectedDriveProxy(HttpClientFactory httpClientFactory, ConnectedDriveConfiguration config) {
- httpClient = httpClientFactory.getCommonHttpClient();
- authHttpClient = httpClientFactory.createHttpClient(AUTH_HTTP_CLIENT_NAME);
- configuration = config;
-
- vehicleUrl = "https://" + BimmerConstants.API_SERVER_MAP.get(configuration.region) + "/webapi/v1/user/vehicles";
- baseUrl = vehicleUrl + "/";
- legacyUrl = "https://" + BimmerConstants.API_SERVER_MAP.get(configuration.region) + "/api/vehicle/dynamic/v1/";
- navigationAPIUrl = "https://" + BimmerConstants.API_SERVER_MAP.get(configuration.region)
- + "/api/vehicle/navigation/v1/";
- remoteCommandUrl = "https://" + BimmerConstants.EADRAX_SERVER_MAP.get(configuration.region)
- + REMOTE_SERVICE_EADRAX_BASE_URL;
- remoteStatusUrl = remoteCommandUrl + "eventStatus";
- }
-
- public synchronized void call(final String url, final boolean post, final @Nullable String encoding,
- final @Nullable String params, final ResponseCallback callback) {
- // only executed in "simulation mode"
- // SimulationTest.testSimulationOff() assures Injector is off when releasing
- if (Injector.isActive()) {
- if (url.equals(baseUrl)) {
- ((StringResponseCallback) callback).onResponse(Injector.getDiscovery());
- } else if (url.endsWith(vehicleStatusAPI)) {
- ((StringResponseCallback) callback).onResponse(Injector.getStatus());
- } else {
- logger.debug("Simulation of {} not supported", url);
- }
- return;
- }
- final Request req;
- final String completeUrl;
-
- if (post) {
- completeUrl = url;
- req = httpClient.POST(url);
- if (encoding != null) {
- if (CONTENT_TYPE_URL_ENCODED.equals(encoding)) {
- req.content(new StringContentProvider(CONTENT_TYPE_URL_ENCODED, params, StandardCharsets.UTF_8));
- } else if (CONTENT_TYPE_JSON_ENCODED.equals(encoding)) {
- req.header(HttpHeader.CONTENT_TYPE, encoding);
- req.content(new StringContentProvider(CONTENT_TYPE_JSON_ENCODED, params, StandardCharsets.UTF_8));
- }
- }
- } else {
- completeUrl = params == null ? url : url + Constants.QUESTION + params;
- req = httpClient.newRequest(completeUrl);
- }
- req.header(HttpHeader.AUTHORIZATION, getToken().getBearerToken());
- req.header(HttpHeader.REFERER, BimmerConstants.LEGACY_REFERER_URL);
-
- req.timeout(HTTP_TIMEOUT_SEC, TimeUnit.SECONDS).send(new BufferingResponseListener() {
- @NonNullByDefault({})
- @Override
- public void onComplete(Result result) {
- if (result.getResponse().getStatus() != 200) {
- NetworkError error = new NetworkError();
- error.url = completeUrl;
- error.status = result.getResponse().getStatus();
- if (result.getResponse().getReason() != null) {
- error.reason = result.getResponse().getReason();
- } else {
- error.reason = result.getFailure().getMessage();
- }
- error.params = result.getRequest().getParams().toString();
- logger.debug("HTTP Error {}", error.toString());
- callback.onError(error);
- } else {
- if (callback instanceof StringResponseCallback) {
- ((StringResponseCallback) callback).onResponse(getContentAsString());
- } else if (callback instanceof ByteResponseCallback) {
- ((ByteResponseCallback) callback).onResponse(getContent());
- } else {
- logger.error("unexpected reponse type {}", callback.getClass().getName());
- }
- }
- }
- });
- }
-
- public void get(String url, @Nullable String coding, @Nullable String params, ResponseCallback callback) {
- call(url, false, coding, params, callback);
- }
-
- public void post(String url, @Nullable String coding, @Nullable String params, ResponseCallback callback) {
- call(url, true, coding, params, callback);
- }
-
- public void requestVehicles(StringResponseCallback callback) {
- get(vehicleUrl, null, null, callback);
- }
-
- public void requestVehcileStatus(VehicleConfiguration config, StringResponseCallback callback) {
- get(baseUrl + config.vin + vehicleStatusAPI, null, null, callback);
- }
-
- public void requestLegacyVehcileStatus(VehicleConfiguration config, StringResponseCallback callback) {
- // see https://github.com/jupe76/bmwcdapi/search?q=dynamic%2Fv1
- get(legacyUrl + config.vin + "?offset=-60", null, null, callback);
- }
-
- public void requestLNavigation(VehicleConfiguration config, StringResponseCallback callback) {
- // see https://github.com/jupe76/bmwcdapi/search?q=dynamic%2Fv1
- get(navigationAPIUrl + config.vin, null, null, callback);
- }
-
- public void requestLastTrip(VehicleConfiguration config, StringResponseCallback callback) {
- get(baseUrl + config.vin + lastTripAPI, null, null, callback);
- }
-
- public void requestAllTrips(VehicleConfiguration config, StringResponseCallback callback) {
- get(baseUrl + config.vin + allTripsAPI, null, null, callback);
- }
-
- public void requestChargingProfile(VehicleConfiguration config, StringResponseCallback callback) {
- get(baseUrl + config.vin + chargeAPI, null, null, callback);
- }
-
- public void requestDestinations(VehicleConfiguration config, StringResponseCallback callback) {
- get(baseUrl + config.vin + destinationAPI, null, null, callback);
- }
-
- public void requestRangeMap(VehicleConfiguration config, @Nullable MultiMap<String> params,
- StringResponseCallback callback) {
- get(baseUrl + config.vin + rangeMapAPI, CONTENT_TYPE_URL_ENCODED,
- UrlEncoded.encode(params, StandardCharsets.UTF_8, false), callback);
- }
-
- public void requestImage(VehicleConfiguration config, ImageProperties props, ByteResponseCallback callback) {
- final String localImageUrl = baseUrl + config.vin + imageAPI;
- final MultiMap<String> dataMap = new MultiMap<String>();
- dataMap.add("width", Integer.toString(props.size));
- dataMap.add("height", Integer.toString(props.size));
- dataMap.add("view", props.viewport);
-
- get(localImageUrl, CONTENT_TYPE_URL_ENCODED, UrlEncoded.encode(dataMap, StandardCharsets.UTF_8, false),
- callback);
- }
-
- RemoteServiceHandler getRemoteServiceHandler(VehicleHandler vehicleHandler) {
- remoteServiceHandler = Optional.of(new RemoteServiceHandler(vehicleHandler, this));
- return remoteServiceHandler.get();
- }
-
- // Token handling
-
- /**
- * Gets new token if old one is expired or invalid. In case of error the token remains.
- * So if token refresh fails the corresponding requests will also fail and update the
- * Thing status accordingly.
- *
- * @return token
- */
- public Token getToken() {
- if (!token.isValid()) {
- if (configuration.preferMyBmw) {
- if (!updateToken()) {
- if (!updateLegacyToken()) {
- logger.debug("Authorization failed!");
- }
- }
- } else {
- if (!updateLegacyToken()) {
- if (!updateToken()) {
- logger.debug("Authorization failed!");
- }
- }
- }
- }
- remoteServiceHandler.ifPresent(serviceHandler -> {
- serviceHandler.setMyBmwApiUsage(token.isMyBmwApiUsage());
- });
- return token;
- }
-
- public synchronized boolean updateToken() {
- if (BimmerConstants.REGION_CHINA.equals(configuration.region)) {
- // region China currently not supported for MyBMW API
- logger.debug("Region {} not supported yet for MyBMW Login", BimmerConstants.REGION_CHINA);
- return false;
- }
- if (!startAuthClient()) {
- return false;
- } // else continue
- String authUri = "https://" + BimmerConstants.AUTH_SERVER_MAP.get(configuration.region)
- + BimmerConstants.OAUTH_ENDPOINT;
-
- Request authRequest = authHttpClient.POST(authUri);
- authRequest.header(HttpHeader.CONTENT_TYPE, CONTENT_TYPE_URL_ENCODED);
-
- MultiMap<String> authChallenge = getTokenBaseValues();
- authChallenge.addAllValues(getTokenAuthValues());
- String authEncoded = UrlEncoded.encode(authChallenge, Charset.defaultCharset(), false);
- authRequest.content(new StringContentProvider(authEncoded));
- try {
- ContentResponse authResponse = authRequest.timeout(HTTP_TIMEOUT_SEC, TimeUnit.SECONDS).send();
- String authResponseString = URLDecoder.decode(authResponse.getContentAsString(), Charset.defaultCharset());
- String authCode = getAuthCode(authResponseString);
- if (!Constants.EMPTY.equals(authCode)) {
- MultiMap<String> codeChallenge = getTokenBaseValues();
- codeChallenge.put(AUTHORIZATION, authCode);
-
- Request codeRequest = authHttpClient.POST(authUri).followRedirects(false);
- codeRequest.header(HttpHeader.CONTENT_TYPE, CONTENT_TYPE_URL_ENCODED);
- String codeEncoded = UrlEncoded.encode(codeChallenge, Charset.defaultCharset(), false);
- codeRequest.content(new StringContentProvider(codeEncoded));
- ContentResponse codeResponse = codeRequest.timeout(HTTP_TIMEOUT_SEC, TimeUnit.SECONDS).send();
- String code = ConnectedDriveProxy.codeFromUrl(codeResponse.getHeaders().get(HttpHeader.LOCATION));
-
- // Get Token
- String tokenUrl = "https://" + BimmerConstants.AUTH_SERVER_MAP.get(configuration.region)
- + BimmerConstants.TOKEN_ENDPOINT;
-
- Request tokenRequest = authHttpClient.POST(tokenUrl).followRedirects(false);
- tokenRequest.header(HttpHeader.CONTENT_TYPE, CONTENT_TYPE_URL_ENCODED);
- tokenRequest.header(HttpHeader.AUTHORIZATION,
- BimmerConstants.AUTHORIZATION_VALUE_MAP.get(configuration.region));
- String tokenEncoded = UrlEncoded.encode(getTokenValues(code), Charset.defaultCharset(), false);
- tokenRequest.content(new StringContentProvider(tokenEncoded));
- ContentResponse tokenResponse = tokenRequest.timeout(HTTP_TIMEOUT_SEC, TimeUnit.SECONDS).send();
- AuthResponse authResponseJson = Converter.getGson().fromJson(tokenResponse.getContentAsString(),
- AuthResponse.class);
- token.setToken(authResponseJson.accessToken);
- token.setType(authResponseJson.tokenType);
- token.setExpiration(authResponseJson.expiresIn);
- token.setMyBmwApiUsage(true);
- return true;
- }
- } catch (InterruptedException | ExecutionException |
-
- TimeoutException e) {
- logger.debug("Authorization exception: {}", e.getMessage());
- }
- return false;
- }
-
- private boolean startAuthClient() {
- if (!authHttpClient.isStarted()) {
- try {
- authHttpClient.start();
- } catch (Exception e) {
- logger.error("Auth HttpClient start failed!");
- return false;
- }
- }
- return true;
- }
-
- private MultiMap<String> getTokenBaseValues() {
- MultiMap<String> baseValues = new MultiMap<String>();
- baseValues.add(CLIENT_ID, Constants.EMPTY + BimmerConstants.CLIENT_ID.get(configuration.region));
- baseValues.add(RESPONSE_TYPE, CODE);
- baseValues.add(REDIRECT_URI, BimmerConstants.REDIRECT_URI_VALUE);
- baseValues.add("state", Constants.EMPTY + BimmerConstants.STATE.get(configuration.region));
- baseValues.add("nonce", "login_nonce");
- baseValues.add(SCOPE, BimmerConstants.SCOPE_VALUES);
- return baseValues;
- }
-
- private MultiMap<String> getTokenAuthValues() {
- MultiMap<String> authValues = new MultiMap<String>();
- authValues.add(GRANT_TYPE, "authorization_code");
- authValues.add(USERNAME, configuration.userName);
- authValues.add(PASSWORD, configuration.password);
- return authValues;
- }
-
- private MultiMap<String> getTokenValues(String code) {
- MultiMap<String> tokenValues = new MultiMap<String>();
- tokenValues.put(CODE, code);
- tokenValues.put("code_verifier", Constants.EMPTY + BimmerConstants.CODE_VERIFIER.get(configuration.region));
- tokenValues.put(REDIRECT_URI, BimmerConstants.REDIRECT_URI_VALUE);
- tokenValues.put(GRANT_TYPE, "authorization_code");
- return tokenValues;
- }
-
- private String getAuthCode(String response) {
- String[] keys = response.split("&");
- for (int i = 0; i < keys.length; i++) {
- if (keys[i].startsWith(AUTHORIZATION)) {
- String authCode = keys[i].split("=")[1];
- authCode = authCode.split("\"")[0];
- return authCode;
- }
- }
- return Constants.EMPTY;
- }
-
- public synchronized boolean updateLegacyToken() {
- logger.debug("updateLegacyToken");
- try {
- /**
- * The authorization with Jetty HttpClient doens't work anymore
- * When calling Jetty with same headers and content a ConcurrentExcpetion is thrown
- * So fallback legacy authorization will stay on java.net handling
- */
- String authUri = "https://" + BimmerConstants.AUTH_SERVER_MAP.get(configuration.region)
- + BimmerConstants.OAUTH_ENDPOINT;
- URL url = new URL(authUri);
- HttpURLConnection.setFollowRedirects(false);
- HttpURLConnection con = (HttpURLConnection) url.openConnection();
- con.setRequestMethod("POST");
- con.setRequestProperty(HttpHeader.CONTENT_TYPE.toString(), CONTENT_TYPE_URL_ENCODED);
- con.setRequestProperty(HttpHeader.CONNECTION.toString(), KEEP_ALIVE);
- con.setRequestProperty(HttpHeader.HOST.toString(),
- BimmerConstants.API_SERVER_MAP.get(configuration.region));
- con.setRequestProperty(HttpHeader.AUTHORIZATION.toString(),
- BimmerConstants.LEGACY_AUTHORIZATION_VALUE_MAP.get(configuration.region));
- con.setRequestProperty(CREDENTIALS, BimmerConstants.LEGACY_CREDENTIAL_VALUES);
- con.setDoOutput(true);
-
- OutputStream os = con.getOutputStream();
- byte[] input = getAuthEncodedData().getBytes("utf-8");
- os.write(input, 0, input.length);
-
- BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"));
- StringBuilder response = new StringBuilder();
- String responseLine = null;
- while ((responseLine = br.readLine()) != null) {
- response.append(responseLine.trim());
- }
- token.setMyBmwApiUsage(false);
- return tokenFromUrl(con.getHeaderField(HttpHeader.LOCATION.toString()));
- } catch (IOException e) {
- logger.warn("{}", e.getMessage());
- }
- return false;
- }
-
- public boolean tokenFromUrl(String encodedUrl) {
- final MultiMap<String> tokenMap = new MultiMap<String>();
- UrlEncoded.decodeTo(encodedUrl, tokenMap, StandardCharsets.US_ASCII);
- tokenMap.forEach((key, value) -> {
- if (value.size() > 0) {
- String val = value.get(0);
- if (key.endsWith(ACCESS_TOKEN)) {
- token.setToken(val.toString());
- } else if (key.equals(EXPIRES_IN)) {
- token.setExpiration(Integer.parseInt(val.toString()));
- } else if (key.equals(TOKEN_TYPE)) {
- token.setType(val.toString());
- }
- }
- });
- logger.info("Token valid? {}", token.isValid());
- return token.isValid();
- }
-
- public static String codeFromUrl(String encodedUrl) {
- final MultiMap<String> tokenMap = new MultiMap<String>();
- UrlEncoded.decodeTo(encodedUrl, tokenMap, StandardCharsets.US_ASCII);
- final StringBuilder codeFound = new StringBuilder();
- tokenMap.forEach((key, value) -> {
- if (value.size() > 0) {
- String val = value.get(0);
- if (key.endsWith(CODE)) {
- codeFound.append(val.toString());
- }
- }
- });
- return codeFound.toString();
- }
-
- private String getAuthEncodedData() {
- MultiMap<String> dataMap = new MultiMap<String>();
- dataMap.add(CLIENT_ID, BimmerConstants.LEGACY_CLIENT_ID);
- dataMap.add(RESPONSE_TYPE, TOKEN);
- dataMap.add(REDIRECT_URI, BimmerConstants.LEGACY_REDIRECT_URI_VALUE);
- dataMap.add(SCOPE, BimmerConstants.LEGACY_SCOPE_VALUES);
- dataMap.add(USERNAME, configuration.userName);
- dataMap.add(PASSWORD, configuration.password);
- return UrlEncoded.encode(dataMap, Charset.defaultCharset(), false);
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.*;
-import static org.openhab.binding.bmwconnecteddrive.internal.utils.HTTPConstants.*;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Optional;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.util.MultiMap;
-import org.eclipse.jetty.util.UrlEncoded;
-import org.openhab.binding.bmwconnecteddrive.internal.VehicleConfiguration;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.NetworkError;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.remote.ExecutionStatusContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.HTTPConstants;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.JsonSyntaxException;
-
-/**
- * The {@link RemoteServiceHandler} handles executions of remote services towards your Vehicle
- *
- * @see https://github.com/bimmerconnected/bimmer_connected/blob/master/bimmer_connected/remote_services.py
- *
- * @author Bernd Weymann - Initial contribution
- * @author Norbert Truchsess - edit & send of charge profile
- */
-@NonNullByDefault
-public class RemoteServiceHandler implements StringResponseCallback {
- private final Logger logger = LoggerFactory.getLogger(RemoteServiceHandler.class);
-
- private static final String SERVICE_TYPE = "serviceType";
- private static final String EVENT_ID = "eventId";
- private static final String DATA = "data";
- // after 6 retries the state update will give up
- private static final int GIVEUP_COUNTER = 6;
- private static final int STATE_UPDATE_SEC = HTTPConstants.HTTP_TIMEOUT_SEC + 1; // regular timeout + 1sec
-
- private final ConnectedDriveProxy proxy;
- private final VehicleHandler handler;
- private final String legacyServiceExecutionAPI;
- private final String legacyServiceExecutionStateAPI;
- private final String serviceExecutionAPI;
- private final String serviceExecutionStateAPI;
-
- private int counter = 0;
- private Optional<ScheduledFuture<?>> stateJob = Optional.empty();
- private Optional<String> serviceExecuting = Optional.empty();
- private Optional<String> executingEventId = Optional.empty();
- private boolean myBmwApiUsage = false;
-
- public enum ExecutionState {
- READY,
- INITIATED,
- PENDING,
- DELIVERED,
- EXECUTED,
- ERROR,
- }
-
- public enum RemoteService {
- LIGHT_FLASH(REMOTE_SERVICE_LIGHT_FLASH, "Flash Lights", "light-flash"),
- VEHICLE_FINDER(REMOTE_SERVICE_VEHICLE_FINDER, "Vehicle Finder", "vehicle-finder"),
- DOOR_LOCK(REMOTE_SERVICE_DOOR_LOCK, "Door Lock", "door-lock"),
- DOOR_UNLOCK(REMOTE_SERVICE_DOOR_UNLOCK, "Door Unlock", "door-unlock"),
- HORN_BLOW(REMOTE_SERVICE_HORN, "Horn Blow", "horn-blow"),
- CLIMATE_NOW(REMOTE_SERVICE_AIR_CONDITIONING, "Climate Control", "air-conditioning"),
- CHARGE_NOW(REMOTE_SERVICE_CHARGE_NOW, "Start Charging", "charge-now"),
- CHARGING_CONTROL(REMOTE_SERVICE_CHARGING_CONTROL, "Send Charging Profile", "charging-control");
-
- private final String command;
- private final String label;
- private final String remoteCommand;
-
- RemoteService(final String command, final String label, final String remoteCommand) {
- this.command = command;
- this.label = label;
- this.remoteCommand = remoteCommand;
- }
-
- public String getCommand() {
- return command;
- }
-
- public String getLabel() {
- return label;
- }
-
- public String getRemoteCommand() {
- return remoteCommand;
- }
- }
-
- public RemoteServiceHandler(VehicleHandler vehicleHandler, ConnectedDriveProxy connectedDriveProxy) {
- handler = vehicleHandler;
- proxy = connectedDriveProxy;
- final VehicleConfiguration config = handler.getConfiguration().get();
- legacyServiceExecutionAPI = proxy.baseUrl + config.vin + proxy.serviceExecutionAPI;
- legacyServiceExecutionStateAPI = proxy.baseUrl + config.vin + proxy.serviceExecutionStateAPI;
- serviceExecutionAPI = proxy.remoteCommandUrl + config.vin + "/";
- serviceExecutionStateAPI = proxy.remoteStatusUrl;
- }
-
- boolean execute(RemoteService service, String... data) {
- synchronized (this) {
- if (serviceExecuting.isPresent()) {
- logger.debug("Execution rejected - {} still pending", serviceExecuting.get());
- // only one service executing
- return false;
- }
- serviceExecuting = Optional.of(service.name());
- }
- if (myBmwApiUsage) {
- final MultiMap<String> dataMap = new MultiMap<String>();
- if (data.length > 0) {
- dataMap.add(DATA, data[0]);
- proxy.post(serviceExecutionAPI + service.getRemoteCommand(), CONTENT_TYPE_JSON_ENCODED,
- "{CHARGING_PROFILE:" + data[0] + "}", this);
- } else {
- proxy.post(serviceExecutionAPI + service.getRemoteCommand(), null, null, this);
- }
- } else {
- final MultiMap<String> dataMap = new MultiMap<String>();
- dataMap.add(SERVICE_TYPE, service.name());
- if (data.length > 0) {
- dataMap.add(DATA, data[0]);
- }
- proxy.post(legacyServiceExecutionAPI, CONTENT_TYPE_URL_ENCODED,
- UrlEncoded.encode(dataMap, StandardCharsets.UTF_8, false), this);
- }
- return true;
- }
-
- public void getState() {
- synchronized (this) {
- serviceExecuting.ifPresentOrElse(service -> {
- if (counter >= GIVEUP_COUNTER) {
- logger.warn("Giving up updating state for {} after {} times", service, GIVEUP_COUNTER);
- reset();
- // immediately refresh data
- handler.getData();
- }
- counter++;
- if (myBmwApiUsage) {
- final MultiMap<String> dataMap = new MultiMap<String>();
- dataMap.add(EVENT_ID, executingEventId.get());
- final String encoded = dataMap == null || dataMap.isEmpty() ? null
- : UrlEncoded.encode(dataMap, StandardCharsets.UTF_8, false);
-
- proxy.post(serviceExecutionStateAPI + Constants.QUESTION + encoded, null, null, this);
- } else {
- final MultiMap<String> dataMap = new MultiMap<String>();
- dataMap.add(SERVICE_TYPE, service);
- proxy.get(legacyServiceExecutionStateAPI, CONTENT_TYPE_URL_ENCODED,
- UrlEncoded.encode(dataMap, StandardCharsets.UTF_8, false), this);
- }
- }, () -> {
- logger.warn("No Service executed to get state");
- });
- stateJob = Optional.empty();
- }
- }
-
- @Override
- public void onResponse(@Nullable String result) {
- if (result != null) {
- try {
- ExecutionStatusContainer esc = Converter.getGson().fromJson(result, ExecutionStatusContainer.class);
- if (esc != null) {
- if (esc.executionStatus != null) {
- // handling of BMW ConnectedDrive updates
- String status = esc.executionStatus.status;
- if (status != null) {
- synchronized (this) {
- handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null), status);
- if (ExecutionState.EXECUTED.name().equals(status)) {
- // refresh loop ends - update of status handled in the normal refreshInterval.
- // Earlier
- // update doesn't show better results!
- reset();
- return;
- }
- }
- }
- } else if (esc.eventId != null) {
- // store event id for further MyBMW updates
- executingEventId = Optional.of(esc.eventId);
- } else if (esc.eventStatus != null) {
- // update status for MyBMW API
- synchronized (this) {
- handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null), esc.eventStatus);
- if (ExecutionState.EXECUTED.name().equals(esc.eventStatus)) {
- // refresh loop ends - update of status handled in the normal refreshInterval.
- // Earlier
- // update doesn't show better results!
- reset();
- return;
- }
- }
- }
- }
- } catch (JsonSyntaxException jse) {
- logger.debug("RemoteService response is unparseable: {} {}", result, jse.getMessage());
- }
- }
- // schedule even if no result is present until retries exceeded
- synchronized (this) {
- stateJob.ifPresent(job -> {
- if (!job.isDone()) {
- job.cancel(true);
- }
- });
- stateJob = Optional.of(handler.getScheduler().schedule(this::getState, STATE_UPDATE_SEC, TimeUnit.SECONDS));
- }
- }
-
- @Override
- public void onError(NetworkError error) {
- synchronized (this) {
- handler.updateRemoteExecutionStatus(serviceExecuting.orElse(null),
- ExecutionState.ERROR.name() + Constants.SPACE + Integer.toString(error.status));
- reset();
- }
- }
-
- private void reset() {
- serviceExecuting = Optional.empty();
- executingEventId = Optional.empty();
- counter = 0;
- }
-
- public void cancel() {
- synchronized (this) {
- stateJob.ifPresent(action -> {
- if (!action.isDone()) {
- action.cancel(true);
- }
- stateJob = Optional.empty();
- });
- }
- }
-
- public void setMyBmwApiUsage(boolean b) {
- myBmwApiUsage = b;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.NetworkError;
-
-/**
- * The {@link ResponseCallback} Marker Interface for all ASYNC REST API callbacks
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public interface ResponseCallback {
- public void onError(NetworkError error);
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link StringResponseCallback} Interface for all String results from ASYNC REST API
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public interface StringResponseCallback extends ResponseCallback {
-
- public void onResponse(@Nullable String result);
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-
-/**
- * The {@link Token} BMW ConnectedDrive Token storage
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class Token {
- private String token = Constants.EMPTY;
- private String tokenType = Constants.EMPTY;
- private long expiration = 0;
- private boolean myBmwApiUsage = false;
-
- public boolean isMyBmwApiUsage() {
- return myBmwApiUsage;
- }
-
- public void setMyBmwApiUsage(boolean myBmwAppUsage) {
- this.myBmwApiUsage = myBmwAppUsage;
- }
-
- public String getBearerToken() {
- return new StringBuilder(tokenType).append(Constants.SPACE).append(token).toString();
- }
-
- public void setToken(String token) {
- this.token = token;
- }
-
- public void setExpiration(int expiration) {
- this.expiration = System.currentTimeMillis() / 1000 + expiration;
- }
-
- public void setType(String type) {
- tokenType = type;
- }
-
- public boolean isValid() {
- return (!token.equals(Constants.EMPTY) && !tokenType.equals(Constants.EMPTY)
- && (this.expiration - System.currentTimeMillis() / 1000) > 1);
- }
-
- @Override
- public String toString() {
- return tokenType + token;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.*;
-
-import java.time.DayOfWeek;
-import java.time.LocalTime;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-
-import javax.measure.quantity.Length;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.VehicleType;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.Destination;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.AllTrips;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.LastTrip;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.CBSMessage;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.CCMMessage;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.Doors;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.Position;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatus;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.Windows;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileUtils;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileUtils.TimedChannel;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileWrapper;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileWrapper.ProfileKey;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.RemoteServiceUtils;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.VehicleStatusUtils;
-import org.openhab.core.library.types.DateTimeType;
-import org.openhab.core.library.types.OnOffType;
-import org.openhab.core.library.types.PointType;
-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.Units;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.binding.BaseThingHandler;
-import org.openhab.core.types.State;
-import org.openhab.core.types.StateOption;
-import org.openhab.core.types.UnDefType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.JsonSyntaxException;
-
-/**
- * The {@link VehicleChannelHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- * @author Norbert Truchsess - edit & send of charge profile
- */
-@NonNullByDefault
-public abstract class VehicleChannelHandler extends BaseThingHandler {
- protected final Logger logger = LoggerFactory.getLogger(VehicleChannelHandler.class);
- protected boolean imperial = false;
- protected boolean hasFuel = false;
- protected boolean isElectric = false;
- protected boolean isHybrid = false;
-
- // List Interfaces
- protected List<CBSMessage> serviceList = new ArrayList<CBSMessage>();
- protected String selectedService = Constants.UNDEF;
- protected List<CCMMessage> checkControlList = new ArrayList<CCMMessage>();
- protected String selectedCC = Constants.UNDEF;
- protected List<Destination> destinationList = new ArrayList<Destination>();
- protected String selectedDestination = Constants.UNDEF;
-
- protected BMWConnectedDriveOptionProvider optionProvider;
-
- // Data Caches
- protected Optional<String> vehicleStatusCache = Optional.empty();
- protected Optional<String> lastTripCache = Optional.empty();
- protected Optional<String> allTripsCache = Optional.empty();
- protected Optional<String> chargeProfileCache = Optional.empty();
- protected Optional<String> rangeMapCache = Optional.empty();
- protected Optional<String> destinationCache = Optional.empty();
- protected Optional<byte[]> imageCache = Optional.empty();
-
- public VehicleChannelHandler(Thing thing, BMWConnectedDriveOptionProvider op, String type, boolean imperial) {
- super(thing);
- optionProvider = op;
-
- this.imperial = imperial;
- hasFuel = type.equals(VehicleType.CONVENTIONAL.toString()) || type.equals(VehicleType.PLUGIN_HYBRID.toString())
- || type.equals(VehicleType.ELECTRIC_REX.toString());
- isElectric = type.equals(VehicleType.PLUGIN_HYBRID.toString())
- || type.equals(VehicleType.ELECTRIC_REX.toString()) || type.equals(VehicleType.ELECTRIC.toString());
- isHybrid = hasFuel && isElectric;
-
- setOptions(CHANNEL_GROUP_REMOTE, REMOTE_SERVICE_COMMAND, RemoteServiceUtils.getOptions(isElectric));
- }
-
- private void setOptions(final String group, final String id, List<StateOption> options) {
- optionProvider.setStateOptions(new ChannelUID(thing.getUID(), group, id), options);
- }
-
- protected void updateChannel(final String group, final String id, final State state) {
- updateState(new ChannelUID(thing.getUID(), group, id), state);
- }
-
- protected void updateCheckControls(List<CCMMessage> ccl) {
- if (ccl.isEmpty()) {
- // No Check Control available - show not active
- CCMMessage ccm = new CCMMessage();
- ccm.ccmDescriptionLong = Constants.NO_ENTRIES;
- ccm.ccmDescriptionShort = Constants.NO_ENTRIES;
- ccm.ccmId = -1;
- ccm.ccmMileage = -1;
- ccl.add(ccm);
- }
-
- // add all elements to options
- checkControlList = ccl;
- List<StateOption> ccmDescriptionOptions = new ArrayList<>();
- List<StateOption> ccmDetailsOptions = new ArrayList<>();
- List<StateOption> ccmMileageOptions = new ArrayList<>();
- boolean isSelectedElementIn = false;
- int index = 0;
- for (CCMMessage ccEntry : checkControlList) {
- ccmDescriptionOptions.add(new StateOption(Integer.toString(index), ccEntry.ccmDescriptionShort));
- ccmDetailsOptions.add(new StateOption(Integer.toString(index), ccEntry.ccmDescriptionLong));
- ccmMileageOptions.add(new StateOption(Integer.toString(index), Integer.toString(ccEntry.ccmMileage)));
- if (selectedCC.equals(ccEntry.ccmDescriptionShort)) {
- isSelectedElementIn = true;
- }
- index++;
- }
- setOptions(CHANNEL_GROUP_CHECK_CONTROL, NAME, ccmDescriptionOptions);
- setOptions(CHANNEL_GROUP_CHECK_CONTROL, DETAILS, ccmDetailsOptions);
- setOptions(CHANNEL_GROUP_CHECK_CONTROL, MILEAGE, ccmMileageOptions);
-
- // if current selected item isn't anymore in the list select first entry
- if (!isSelectedElementIn) {
- selectCheckControl(0);
- }
- }
-
- protected void selectCheckControl(int index) {
- if (index >= 0 && index < checkControlList.size()) {
- CCMMessage ccEntry = checkControlList.get(index);
- selectedCC = ccEntry.ccmDescriptionShort;
- updateChannel(CHANNEL_GROUP_CHECK_CONTROL, NAME, StringType.valueOf(ccEntry.ccmDescriptionShort));
- updateChannel(CHANNEL_GROUP_CHECK_CONTROL, DETAILS, StringType.valueOf(ccEntry.ccmDescriptionLong));
- updateChannel(CHANNEL_GROUP_CHECK_CONTROL, MILEAGE, QuantityType.valueOf(
- Converter.round(ccEntry.ccmMileage), imperial ? ImperialUnits.MILE : Constants.KILOMETRE_UNIT));
- }
- }
-
- protected void updateServices(List<CBSMessage> sl) {
- // if list is empty add "undefined" element
- if (sl.isEmpty()) {
- CBSMessage cbsm = new CBSMessage();
- cbsm.cbsType = Constants.NO_ENTRIES;
- cbsm.cbsDescription = Constants.NO_ENTRIES;
- sl.add(cbsm);
- }
-
- // add all elements to options
- serviceList = sl;
- List<StateOption> serviceNameOptions = new ArrayList<>();
- List<StateOption> serviceDetailsOptions = new ArrayList<>();
- List<StateOption> serviceDateOptions = new ArrayList<>();
- List<StateOption> serviceMileageOptions = new ArrayList<>();
- boolean isSelectedElementIn = false;
- int index = 0;
- for (CBSMessage serviceEntry : serviceList) {
- // create StateOption with "value = list index" and "label = human readable string"
- serviceNameOptions.add(new StateOption(Integer.toString(index), serviceEntry.getType()));
- serviceDetailsOptions.add(new StateOption(Integer.toString(index), serviceEntry.getDescription()));
- serviceDateOptions.add(new StateOption(Integer.toString(index), serviceEntry.getDueDate()));
- serviceMileageOptions
- .add(new StateOption(Integer.toString(index), Integer.toString(serviceEntry.cbsRemainingMileage)));
- if (selectedService.equals(serviceEntry.getType())) {
- isSelectedElementIn = true;
- }
- index++;
- }
- setOptions(CHANNEL_GROUP_SERVICE, NAME, serviceNameOptions);
- setOptions(CHANNEL_GROUP_SERVICE, DETAILS, serviceDetailsOptions);
- setOptions(CHANNEL_GROUP_SERVICE, DATE, serviceDateOptions);
- setOptions(CHANNEL_GROUP_SERVICE, MILEAGE, serviceMileageOptions);
-
- // if current selected item isn't anymore in the list select first entry
- if (!isSelectedElementIn) {
- selectService(0);
- }
- }
-
- protected void selectService(int index) {
- if (index >= 0 && index < serviceList.size()) {
- CBSMessage serviceEntry = serviceList.get(index);
- selectedService = serviceEntry.cbsType;
- updateChannel(CHANNEL_GROUP_SERVICE, NAME,
- StringType.valueOf(Converter.toTitleCase(serviceEntry.getType())));
- updateChannel(CHANNEL_GROUP_SERVICE, DETAILS,
- StringType.valueOf(Converter.toTitleCase(serviceEntry.getDescription())));
- updateChannel(CHANNEL_GROUP_SERVICE, DATE,
- DateTimeType.valueOf(Converter.getLocalDateTime(serviceEntry.getDueDate())));
- updateChannel(CHANNEL_GROUP_SERVICE, MILEAGE,
- QuantityType.valueOf(Converter.round(serviceEntry.cbsRemainingMileage),
- imperial ? ImperialUnits.MILE : Constants.KILOMETRE_UNIT));
- }
- }
-
- protected void updateDestinations(List<Destination> dl) {
- // if list is empty add "undefined" element
- if (dl.isEmpty()) {
- Destination dest = new Destination();
- dest.city = Constants.NO_ENTRIES;
- dest.lat = -1;
- dest.lon = -1;
- dl.add(dest);
- }
-
- // add all elements to options
- destinationList = dl;
- List<StateOption> destinationNameOptions = new ArrayList<>();
- List<StateOption> destinationGPSOptions = new ArrayList<>();
- boolean isSelectedElementIn = false;
- int index = 0;
- for (Destination destination : destinationList) {
- destinationNameOptions.add(new StateOption(Integer.toString(index), destination.getAddress()));
- destinationGPSOptions.add(new StateOption(Integer.toString(index), destination.getCoordinates()));
- if (selectedDestination.equals(destination.getAddress())) {
- isSelectedElementIn = true;
- }
- index++;
- }
- setOptions(CHANNEL_GROUP_DESTINATION, NAME, destinationNameOptions);
- setOptions(CHANNEL_GROUP_DESTINATION, GPS, destinationGPSOptions);
-
- // if current selected item isn't anymore in the list select first entry
- if (!isSelectedElementIn) {
- selectDestination(0);
- }
- }
-
- protected void selectDestination(int index) {
- if (index >= 0 && index < destinationList.size()) {
- Destination destinationEntry = destinationList.get(index);
- // update selected Item
- selectedDestination = destinationEntry.getAddress();
- // update coordinates according to new set location
- updateChannel(CHANNEL_GROUP_DESTINATION, NAME, StringType.valueOf(destinationEntry.getAddress()));
- updateChannel(CHANNEL_GROUP_DESTINATION, GPS, PointType.valueOf(destinationEntry.getCoordinates()));
- }
- }
-
- protected void updateAllTrips(AllTrips allTrips) {
- QuantityType<Length> qtTotalElectric = QuantityType
- .valueOf(Converter.round(allTrips.totalElectricDistance.userTotal), Constants.KILOMETRE_UNIT);
- QuantityType<Length> qtLongestElectricRange = QuantityType
- .valueOf(Converter.round(allTrips.chargecycleRange.userHigh), Constants.KILOMETRE_UNIT);
- QuantityType<Length> qtDistanceSinceCharge = QuantityType
- .valueOf(Converter.round(allTrips.chargecycleRange.userCurrentChargeCycle), Constants.KILOMETRE_UNIT);
-
- updateChannel(CHANNEL_GROUP_LIFETIME, TOTAL_DRIVEN_DISTANCE,
- imperial ? Converter.getMiles(qtTotalElectric) : qtTotalElectric);
- updateChannel(CHANNEL_GROUP_LIFETIME, SINGLE_LONGEST_DISTANCE,
- imperial ? Converter.getMiles(qtLongestElectricRange) : qtLongestElectricRange);
- updateChannel(CHANNEL_GROUP_LAST_TRIP, DISTANCE_SINCE_CHARGING,
- imperial ? Converter.getMiles(qtDistanceSinceCharge) : qtDistanceSinceCharge);
-
- // Conversion from kwh/100km to kwh/10mi has to be done manually
- double avgConsumotion = imperial ? allTrips.avgElectricConsumption.userAverage * Converter.MILES_TO_KM_RATIO
- : allTrips.avgElectricConsumption.userAverage;
- double avgCombinedConsumption = imperial
- ? allTrips.avgCombinedConsumption.userAverage * Converter.MILES_TO_KM_RATIO
- : allTrips.avgCombinedConsumption.userAverage;
- double avgRecuperation = imperial ? allTrips.avgRecuperation.userAverage * Converter.MILES_TO_KM_RATIO
- : allTrips.avgRecuperation.userAverage;
-
- updateChannel(CHANNEL_GROUP_LIFETIME, AVG_CONSUMPTION,
- QuantityType.valueOf(Converter.round(avgConsumotion), Units.KILOWATT_HOUR));
- updateChannel(CHANNEL_GROUP_LIFETIME, AVG_COMBINED_CONSUMPTION,
- QuantityType.valueOf(Converter.round(avgCombinedConsumption), Units.LITRE));
- updateChannel(CHANNEL_GROUP_LIFETIME, AVG_RECUPERATION,
- QuantityType.valueOf(Converter.round(avgRecuperation), Units.KILOWATT_HOUR));
- }
-
- protected void updateLastTrip(LastTrip trip) {
- // Whyever the Last Trip DateTime is delivered without offest - so LocalTime
- updateChannel(CHANNEL_GROUP_LAST_TRIP, DATE,
- DateTimeType.valueOf(Converter.getLocalDateTimeWithoutOffest(trip.date)));
- updateChannel(CHANNEL_GROUP_LAST_TRIP, DURATION, QuantityType.valueOf(trip.duration, Units.MINUTE));
-
- QuantityType<Length> qtTotalDistance = QuantityType.valueOf(Converter.round(trip.totalDistance),
- Constants.KILOMETRE_UNIT);
- updateChannel(CHANNEL_GROUP_LAST_TRIP, DISTANCE,
- imperial ? Converter.getMiles(qtTotalDistance) : qtTotalDistance);
-
- // Conversion from kwh/100km to kwh/10mi has to be done manually
- double avgConsumtption = imperial ? trip.avgElectricConsumption * Converter.MILES_TO_KM_RATIO
- : trip.avgElectricConsumption;
- double avgCombinedConsumption = imperial ? trip.avgCombinedConsumption * Converter.MILES_TO_KM_RATIO
- : trip.avgCombinedConsumption;
- double avgRecuperation = imperial ? trip.avgRecuperation * Converter.MILES_TO_KM_RATIO : trip.avgRecuperation;
-
- updateChannel(CHANNEL_GROUP_LAST_TRIP, AVG_CONSUMPTION,
- QuantityType.valueOf(Converter.round(avgConsumtption), Units.KILOWATT_HOUR));
- updateChannel(CHANNEL_GROUP_LAST_TRIP, AVG_COMBINED_CONSUMPTION,
- QuantityType.valueOf(Converter.round(avgCombinedConsumption), Units.LITRE));
- updateChannel(CHANNEL_GROUP_LAST_TRIP, AVG_RECUPERATION,
- QuantityType.valueOf(Converter.round(avgRecuperation), Units.KILOWATT_HOUR));
- }
-
- protected void updateChargeProfileFromContent(String content) {
- ChargeProfileWrapper.fromJson(content).ifPresent(this::updateChargeProfile);
- }
-
- protected void updateChargeProfile(ChargeProfileWrapper wrapper) {
- updateChannel(CHANNEL_GROUP_CHARGE, CHARGE_PROFILE_PREFERENCE,
- StringType.valueOf(Converter.toTitleCase(wrapper.getPreference())));
- updateChannel(CHANNEL_GROUP_CHARGE, CHARGE_PROFILE_MODE,
- StringType.valueOf(Converter.toTitleCase(wrapper.getMode())));
- final Boolean climate = wrapper.isEnabled(ProfileKey.CLIMATE);
- updateChannel(CHANNEL_GROUP_CHARGE, CHARGE_PROFILE_CLIMATE,
- climate == null ? UnDefType.UNDEF : OnOffType.from(climate));
- updateTimedState(wrapper, ProfileKey.WINDOWSTART);
- updateTimedState(wrapper, ProfileKey.WINDOWEND);
- updateTimedState(wrapper, ProfileKey.TIMER1);
- updateTimedState(wrapper, ProfileKey.TIMER2);
- updateTimedState(wrapper, ProfileKey.TIMER3);
- updateTimedState(wrapper, ProfileKey.OVERRIDE);
- }
-
- protected void updateTimedState(ChargeProfileWrapper profile, ProfileKey key) {
- final TimedChannel timed = ChargeProfileUtils.getTimedChannel(key);
- if (timed != null) {
- final LocalTime time = profile.getTime(key);
- updateChannel(CHANNEL_GROUP_CHARGE, timed.time, time == null ? UnDefType.UNDEF
- : new DateTimeType(ZonedDateTime.of(Constants.EPOCH_DAY, time, ZoneId.systemDefault())));
- if (timed.timer != null) {
- final Boolean enabled = profile.isEnabled(key);
- updateChannel(CHANNEL_GROUP_CHARGE, timed.timer + CHARGE_ENABLED,
- enabled == null ? UnDefType.UNDEF : OnOffType.from(enabled));
- if (timed.hasDays) {
- final Set<DayOfWeek> days = profile.getDays(key);
- updateChannel(CHANNEL_GROUP_CHARGE, timed.timer + CHARGE_DAYS,
- days == null ? UnDefType.UNDEF : StringType.valueOf(ChargeProfileUtils.formatDays(days)));
- EnumSet.allOf(DayOfWeek.class).forEach(day -> {
- updateChannel(CHANNEL_GROUP_CHARGE, timed.timer + ChargeProfileUtils.getDaysChannel(day),
- days == null ? UnDefType.UNDEF : OnOffType.from(days.contains(day)));
- });
- }
- }
- }
- }
-
- protected void updateDoors(Doors doorState) {
- updateChannel(CHANNEL_GROUP_DOORS, DOOR_DRIVER_FRONT,
- StringType.valueOf(Converter.toTitleCase(doorState.doorDriverFront)));
- updateChannel(CHANNEL_GROUP_DOORS, DOOR_DRIVER_REAR,
- StringType.valueOf(Converter.toTitleCase(doorState.doorDriverRear)));
- updateChannel(CHANNEL_GROUP_DOORS, DOOR_PASSENGER_FRONT,
- StringType.valueOf(Converter.toTitleCase(doorState.doorPassengerFront)));
- updateChannel(CHANNEL_GROUP_DOORS, DOOR_PASSENGER_REAR,
- StringType.valueOf(Converter.toTitleCase(doorState.doorPassengerRear)));
- updateChannel(CHANNEL_GROUP_DOORS, TRUNK, StringType.valueOf(Converter.toTitleCase(doorState.trunk)));
- updateChannel(CHANNEL_GROUP_DOORS, HOOD, StringType.valueOf(Converter.toTitleCase(doorState.hood)));
- }
-
- protected void updateWindows(Windows windowState) {
- updateChannel(CHANNEL_GROUP_DOORS, WINDOW_DOOR_DRIVER_FRONT,
- StringType.valueOf(Converter.toTitleCase(windowState.windowDriverFront)));
- updateChannel(CHANNEL_GROUP_DOORS, WINDOW_DOOR_DRIVER_REAR,
- StringType.valueOf(Converter.toTitleCase(windowState.windowDriverRear)));
- updateChannel(CHANNEL_GROUP_DOORS, WINDOW_DOOR_PASSENGER_FRONT,
- StringType.valueOf(Converter.toTitleCase(windowState.windowPassengerFront)));
- updateChannel(CHANNEL_GROUP_DOORS, WINDOW_DOOR_PASSENGER_REAR,
- StringType.valueOf(Converter.toTitleCase(windowState.windowPassengerRear)));
- updateChannel(CHANNEL_GROUP_DOORS, WINDOW_REAR,
- StringType.valueOf(Converter.toTitleCase(windowState.rearWindow)));
- updateChannel(CHANNEL_GROUP_DOORS, SUNROOF, StringType.valueOf(Converter.toTitleCase(windowState.sunroof)));
- }
-
- protected void updatePosition(Position pos) {
- updateChannel(CHANNEL_GROUP_LOCATION, GPS, PointType.valueOf(pos.getCoordinates()));
- updateChannel(CHANNEL_GROUP_LOCATION, HEADING, QuantityType.valueOf(pos.heading, Units.DEGREE_ANGLE));
- }
-
- protected void updateVehicleStatus(VehicleStatus vStatus) {
- // Vehicle Status
- updateChannel(CHANNEL_GROUP_STATUS, LOCK, StringType.valueOf(Converter.toTitleCase(vStatus.doorLockState)));
-
- // Service Updates
- updateChannel(CHANNEL_GROUP_STATUS, SERVICE_DATE,
- DateTimeType.valueOf(Converter.getLocalDateTime(VehicleStatusUtils.getNextServiceDate(vStatus))));
-
- updateChannel(CHANNEL_GROUP_STATUS, SERVICE_MILEAGE,
- QuantityType.valueOf(Converter.round(VehicleStatusUtils.getNextServiceMileage(vStatus)),
- imperial ? ImperialUnits.MILE : Constants.KILOMETRE_UNIT));
- // CheckControl Active?
- updateChannel(CHANNEL_GROUP_STATUS, CHECK_CONTROL,
- StringType.valueOf(Converter.toTitleCase(VehicleStatusUtils.checkControlActive(vStatus))));
- // last update Time
- updateChannel(CHANNEL_GROUP_STATUS, LAST_UPDATE,
- DateTimeType.valueOf(Converter.getLocalDateTime(VehicleStatusUtils.getUpdateTime(vStatus))));
- // last update reason
- updateChannel(CHANNEL_GROUP_STATUS, LAST_UPDATE_REASON,
- StringType.valueOf(Converter.toTitleCase(vStatus.updateReason)));
-
- Doors doorState = null;
- try {
- doorState = Converter.getGson().fromJson(Converter.getGson().toJson(vStatus), Doors.class);
- } catch (JsonSyntaxException jse) {
- logger.debug("Doors parse exception {}", jse.getMessage());
- }
- if (doorState != null) {
- updateChannel(CHANNEL_GROUP_STATUS, DOORS, StringType.valueOf(VehicleStatusUtils.checkClosed(doorState)));
- updateDoors(doorState);
- }
- Windows windowState = null;
- try {
- windowState = Converter.getGson().fromJson(Converter.getGson().toJson(vStatus), Windows.class);
- } catch (JsonSyntaxException jse) {
- logger.debug("Windows parse exception {}", jse.getMessage());
- }
- if (windowState != null) {
- updateChannel(CHANNEL_GROUP_STATUS, WINDOWS,
- StringType.valueOf(VehicleStatusUtils.checkClosed(windowState)));
- updateWindows(windowState);
- }
-
- // Range values
- // based on unit of length decide if range shall be reported in km or miles
- double totalRange = 0;
- double maxTotalRange = 0;
- if (isElectric) {
- totalRange += vStatus.remainingRangeElectric;
- QuantityType<Length> qtElectricRange = QuantityType.valueOf(vStatus.remainingRangeElectric,
- Constants.KILOMETRE_UNIT);
- QuantityType<Length> qtElectricRadius = QuantityType
- .valueOf(Converter.guessRangeRadius(vStatus.remainingRangeElectric), Constants.KILOMETRE_UNIT);
-
- updateChannel(CHANNEL_GROUP_RANGE, RANGE_ELECTRIC,
- imperial ? Converter.getMiles(qtElectricRange) : qtElectricRange);
- updateChannel(CHANNEL_GROUP_RANGE, RANGE_RADIUS_ELECTRIC,
- imperial ? Converter.getMiles(qtElectricRadius) : qtElectricRadius);
-
- maxTotalRange += vStatus.maxRangeElectric;
- QuantityType<Length> qtMaxElectricRange = QuantityType.valueOf(vStatus.maxRangeElectric,
- Constants.KILOMETRE_UNIT);
- QuantityType<Length> qtMaxElectricRadius = QuantityType
- .valueOf(Converter.guessRangeRadius(vStatus.maxRangeElectric), Constants.KILOMETRE_UNIT);
-
- updateChannel(CHANNEL_GROUP_RANGE, RANGE_ELECTRIC_MAX,
- imperial ? Converter.getMiles(qtMaxElectricRange) : qtMaxElectricRange);
- updateChannel(CHANNEL_GROUP_RANGE, RANGE_RADIUS_ELECTRIC_MAX,
- imperial ? Converter.getMiles(qtMaxElectricRadius) : qtMaxElectricRadius);
- }
- if (hasFuel) {
- totalRange += vStatus.remainingRangeFuel;
- maxTotalRange += vStatus.remainingRangeFuel;
- QuantityType<Length> qtFuelRange = QuantityType.valueOf(vStatus.remainingRangeFuel,
- Constants.KILOMETRE_UNIT);
- QuantityType<Length> qtFuelRadius = QuantityType
- .valueOf(Converter.guessRangeRadius(vStatus.remainingRangeFuel), Constants.KILOMETRE_UNIT);
-
- updateChannel(CHANNEL_GROUP_RANGE, RANGE_FUEL, imperial ? Converter.getMiles(qtFuelRange) : qtFuelRange);
- updateChannel(CHANNEL_GROUP_RANGE, RANGE_RADIUS_FUEL,
- imperial ? Converter.getMiles(qtFuelRadius) : qtFuelRadius);
- }
- if (isHybrid) {
- QuantityType<Length> qtHybridRange = QuantityType.valueOf(totalRange, Constants.KILOMETRE_UNIT);
- QuantityType<Length> qtHybridRadius = QuantityType.valueOf(Converter.guessRangeRadius(totalRange),
- Constants.KILOMETRE_UNIT);
- QuantityType<Length> qtMaxHybridRange = QuantityType.valueOf(maxTotalRange, Constants.KILOMETRE_UNIT);
- QuantityType<Length> qtMaxHybridRadius = QuantityType.valueOf(Converter.guessRangeRadius(maxTotalRange),
- Constants.KILOMETRE_UNIT);
- updateChannel(CHANNEL_GROUP_RANGE, RANGE_HYBRID,
- imperial ? Converter.getMiles(qtHybridRange) : qtHybridRange);
- updateChannel(CHANNEL_GROUP_RANGE, RANGE_RADIUS_HYBRID,
- imperial ? Converter.getMiles(qtHybridRadius) : qtHybridRadius);
- updateChannel(CHANNEL_GROUP_RANGE, RANGE_HYBRID_MAX,
- imperial ? Converter.getMiles(qtMaxHybridRange) : qtMaxHybridRange);
- updateChannel(CHANNEL_GROUP_RANGE, RANGE_RADIUS_HYBRID_MAX,
- imperial ? Converter.getMiles(qtMaxHybridRadius) : qtMaxHybridRadius);
- }
-
- updateChannel(CHANNEL_GROUP_RANGE, MILEAGE,
- QuantityType.valueOf(vStatus.mileage, imperial ? ImperialUnits.MILE : Constants.KILOMETRE_UNIT));
- if (isElectric) {
- updateChannel(CHANNEL_GROUP_RANGE, SOC, QuantityType.valueOf(vStatus.chargingLevelHv, Units.PERCENT));
- }
- if (hasFuel) {
- updateChannel(CHANNEL_GROUP_RANGE, REMAINING_FUEL,
- QuantityType.valueOf(vStatus.remainingFuel, Units.LITRE));
- }
-
- // Charge Values
- if (isElectric) {
- if (vStatus.connectionStatus != null) {
- updateChannel(CHANNEL_GROUP_STATUS, PLUG_CONNECTION,
- StringType.valueOf(Converter.toTitleCase(vStatus.connectionStatus)));
- } else {
- updateChannel(CHANNEL_GROUP_STATUS, PLUG_CONNECTION, UnDefType.NULL);
- }
- if (vStatus.chargingStatus != null) {
- if (Constants.INVALID.equals(vStatus.chargingStatus)) {
- updateChannel(CHANNEL_GROUP_STATUS, CHARGE_STATUS,
- StringType.valueOf(Converter.toTitleCase(vStatus.lastChargingEndReason)));
- } else {
- // State INVALID is somehow misleading. Instead show the Last Charging End Reason
- updateChannel(CHANNEL_GROUP_STATUS, CHARGE_STATUS,
- StringType.valueOf(Converter.toTitleCase(vStatus.chargingStatus)));
- }
- } else {
- updateChannel(CHANNEL_GROUP_STATUS, CHARGE_STATUS, UnDefType.NULL);
- }
- if (vStatus.chargingTimeRemaining != null) {
- try {
- updateChannel(CHANNEL_GROUP_STATUS, CHARGE_REMAINING,
- QuantityType.valueOf(vStatus.chargingTimeRemaining, Units.MINUTE));
- } catch (NumberFormatException nfe) {
- updateChannel(CHANNEL_GROUP_STATUS, CHARGE_REMAINING, UnDefType.UNDEF);
- }
- } else {
- updateChannel(CHANNEL_GROUP_STATUS, CHARGE_REMAINING, UnDefType.NULL);
- }
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.*;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.bmwconnecteddrive.internal.VehicleConfiguration;
-import org.openhab.binding.bmwconnecteddrive.internal.action.BMWConnectedDriveActions;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.DestinationContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.NetworkError;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.compat.VehicleAttributesContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.navigation.NavigationContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.AllTrips;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.AllTripsContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.LastTrip;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.LastTripContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatus;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatusContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.RemoteServiceHandler.ExecutionState;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.RemoteServiceHandler.RemoteService;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileUtils;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileUtils.ChargeKeyDay;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileWrapper;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileWrapper.ProfileKey;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ImageProperties;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.RemoteServiceUtils;
-import org.openhab.core.io.net.http.HttpUtil;
-import org.openhab.core.library.types.DateTimeType;
-import org.openhab.core.library.types.DecimalType;
-import org.openhab.core.library.types.OnOffType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.types.RawType;
-import org.openhab.core.library.types.StringType;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.Bridge;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.binding.BridgeHandler;
-import org.openhab.core.thing.binding.ThingHandlerService;
-import org.openhab.core.types.Command;
-import org.openhab.core.types.RefreshType;
-
-import com.google.gson.JsonSyntaxException;
-
-/**
- * The {@link VehicleHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- * @author Norbert Truchsess - edit & send charge profile
- */
-@NonNullByDefault
-public class VehicleHandler extends VehicleChannelHandler {
- private int legacyMode = Constants.INT_UNDEF; // switch to legacy API in case of 404 Errors
-
- private Optional<ConnectedDriveProxy> proxy = Optional.empty();
- private Optional<RemoteServiceHandler> remote = Optional.empty();
- private Optional<VehicleConfiguration> configuration = Optional.empty();
- private Optional<ConnectedDriveBridgeHandler> bridgeHandler = Optional.empty();
- private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
- private Optional<ScheduledFuture<?>> editTimeout = Optional.empty();
- private Optional<List<ResponseCallback>> callbackCounter = Optional.empty();
-
- private ImageProperties imageProperties = new ImageProperties();
- VehicleStatusCallback vehicleStatusCallback = new VehicleStatusCallback();
- StringResponseCallback oldVehicleStatusCallback = new LegacyVehicleStatusCallback();
- StringResponseCallback navigationCallback = new NavigationStatusCallback();
- StringResponseCallback lastTripCallback = new LastTripCallback();
- StringResponseCallback allTripsCallback = new AllTripsCallback();
- StringResponseCallback chargeProfileCallback = new ChargeProfilesCallback();
- StringResponseCallback rangeMapCallback = new RangeMapCallback();
- DestinationsCallback destinationCallback = new DestinationsCallback();
- ByteResponseCallback imageCallback = new ImageCallback();
-
- private Optional<ChargeProfileWrapper> chargeProfileEdit = Optional.empty();
- private Optional<String> chargeProfileSent = Optional.empty();
-
- public VehicleHandler(Thing thing, BMWConnectedDriveOptionProvider op, String type, boolean imperial) {
- super(thing, op, type, imperial);
- }
-
- @Override
- public void handleCommand(ChannelUID channelUID, Command command) {
- String group = channelUID.getGroupId();
-
- // Refresh of Channels with cached values
- if (command instanceof RefreshType) {
- if (CHANNEL_GROUP_LAST_TRIP.equals(group)) {
- lastTripCache.ifPresent(lastTrip -> lastTripCallback.onResponse(lastTrip));
- } else if (CHANNEL_GROUP_LIFETIME.equals(group)) {
- allTripsCache.ifPresent(allTrips -> allTripsCallback.onResponse(allTrips));
- } else if (CHANNEL_GROUP_DESTINATION.equals(group)) {
- destinationCache.ifPresent(destination -> destinationCallback.onResponse(destination));
- } else if (CHANNEL_GROUP_STATUS.equals(group)) {
- vehicleStatusCache.ifPresent(vehicleStatus -> vehicleStatusCallback.onResponse(vehicleStatus));
- } else if (CHANNEL_GROUP_CHARGE.equals(group)) {
- chargeProfileEdit.ifPresentOrElse(this::updateChargeProfile,
- () -> chargeProfileCache.ifPresent(this::updateChargeProfileFromContent));
- } else if (CHANNEL_GROUP_VEHICLE_IMAGE.equals(group)) {
- imageCache.ifPresent(image -> imageCallback.onResponse(image));
- }
- // Check for Channel Group and corresponding Actions
- } else if (CHANNEL_GROUP_REMOTE.equals(group)) {
- // Executing Remote Services
- if (command instanceof StringType) {
- String serviceCommand = ((StringType) command).toFullString();
- remote.ifPresent(remot -> {
- switch (serviceCommand) {
- case REMOTE_SERVICE_LIGHT_FLASH:
- case REMOTE_SERVICE_AIR_CONDITIONING:
- case REMOTE_SERVICE_DOOR_LOCK:
- case REMOTE_SERVICE_DOOR_UNLOCK:
- case REMOTE_SERVICE_HORN:
- case REMOTE_SERVICE_VEHICLE_FINDER:
- case REMOTE_SERVICE_CHARGE_NOW:
- RemoteServiceUtils.getRemoteService(serviceCommand)
- .ifPresentOrElse(service -> remot.execute(service), () -> {
- logger.debug("Remote service execution {} unknown", serviceCommand);
- });
- break;
- case REMOTE_SERVICE_CHARGING_CONTROL:
- sendChargeProfile(chargeProfileEdit);
- break;
- default:
- logger.debug("Remote service execution {} unknown", serviceCommand);
- break;
- }
- });
- }
- } else if (CHANNEL_GROUP_VEHICLE_IMAGE.equals(group)) {
- // Image Change
- configuration.ifPresent(config -> {
- if (command instanceof StringType) {
- if (channelUID.getIdWithoutGroup().equals(IMAGE_VIEWPORT)) {
- String newViewport = command.toString();
- synchronized (imageProperties) {
- if (!imageProperties.viewport.equals(newViewport)) {
- imageProperties = new ImageProperties(newViewport, imageProperties.size);
- imageCache = Optional.empty();
- proxy.ifPresent(prox -> prox.requestImage(config, imageProperties, imageCallback));
- }
- }
- updateChannel(CHANNEL_GROUP_VEHICLE_IMAGE, IMAGE_VIEWPORT, StringType.valueOf(newViewport));
- }
- }
- if (command instanceof DecimalType) {
- if (command instanceof DecimalType) {
- int newImageSize = ((DecimalType) command).intValue();
- if (channelUID.getIdWithoutGroup().equals(IMAGE_SIZE)) {
- synchronized (imageProperties) {
- if (imageProperties.size != newImageSize) {
- imageProperties = new ImageProperties(imageProperties.viewport, newImageSize);
- imageCache = Optional.empty();
- proxy.ifPresent(prox -> prox.requestImage(config, imageProperties, imageCallback));
- }
- }
- }
- updateChannel(CHANNEL_GROUP_VEHICLE_IMAGE, IMAGE_SIZE, new DecimalType(newImageSize));
- }
- }
- });
- } else if (CHANNEL_GROUP_DESTINATION.equals(group)) {
- if (command instanceof StringType) {
- int index = Converter.getIndex(command.toFullString());
- if (index != -1) {
- selectDestination(index);
- } else {
- logger.debug("Cannot select Destination index {}", command.toFullString());
- }
- }
- } else if (CHANNEL_GROUP_SERVICE.equals(group)) {
- if (command instanceof StringType) {
- int index = Converter.getIndex(command.toFullString());
- if (index != -1) {
- selectService(index);
- } else {
- logger.debug("Cannot select Service index {}", command.toFullString());
- }
- }
- } else if (CHANNEL_GROUP_CHECK_CONTROL.equals(group)) {
- if (command instanceof StringType) {
- int index = Converter.getIndex(command.toFullString());
- if (index != -1) {
- selectCheckControl(index);
- } else {
- logger.debug("Cannot select CheckControl index {}", command.toFullString());
- }
- }
- } else if (CHANNEL_GROUP_CHARGE.equals(group)) {
- handleChargeProfileCommand(channelUID, command);
- }
- }
-
- @Override
- public void initialize() {
- callbackCounter = Optional.of(new ArrayList<ResponseCallback>());
- updateStatus(ThingStatus.UNKNOWN);
- final VehicleConfiguration config = getConfigAs(VehicleConfiguration.class);
- configuration = Optional.of(config);
- Bridge bridge = getBridge();
- if (bridge != null) {
- BridgeHandler handler = bridge.getHandler();
- if (handler != null) {
- bridgeHandler = Optional.of(((ConnectedDriveBridgeHandler) handler));
- proxy = ((ConnectedDriveBridgeHandler) handler).getProxy();
- remote = proxy.map(prox -> prox.getRemoteServiceHandler(this));
- } else {
- logger.debug("Bridge Handler null");
- }
- } else {
- logger.debug("Bridge null");
- }
-
- // get Image after init with config values
- synchronized (imageProperties) {
- imageProperties = new ImageProperties(config.imageViewport, config.imageSize);
- }
- updateChannel(CHANNEL_GROUP_VEHICLE_IMAGE, IMAGE_VIEWPORT, StringType.valueOf((config.imageViewport)));
- updateChannel(CHANNEL_GROUP_VEHICLE_IMAGE, IMAGE_SIZE, new DecimalType((config.imageSize)));
-
- // check imperial setting is different to AutoDetect
- if (!UNITS_AUTODETECT.equals(config.units)) {
- imperial = UNITS_IMPERIAL.equals(config.units);
- }
-
- // start update schedule
- startSchedule(config.refreshInterval);
- }
-
- private void startSchedule(int interval) {
- refreshJob.ifPresentOrElse(job -> {
- if (job.isCancelled()) {
- refreshJob = Optional
- .of(scheduler.scheduleWithFixedDelay(this::getData, 0, interval, TimeUnit.MINUTES));
- } // else - scheduler is already running!
- }, () -> {
- refreshJob = Optional.of(scheduler.scheduleWithFixedDelay(this::getData, 0, interval, TimeUnit.MINUTES));
- });
- }
-
- @Override
- public void dispose() {
- refreshJob.ifPresent(job -> job.cancel(true));
- editTimeout.ifPresent(job -> job.cancel(true));
- remote.ifPresent(RemoteServiceHandler::cancel);
- }
-
- public void getData() {
- proxy.ifPresentOrElse(prox -> {
- configuration.ifPresentOrElse(config -> {
- if (legacyMode == 1) {
- prox.requestLegacyVehcileStatus(config, oldVehicleStatusCallback);
- } else {
- prox.requestVehcileStatus(config, vehicleStatusCallback);
- }
- addCallback(vehicleStatusCallback);
- prox.requestLNavigation(config, navigationCallback);
- addCallback(navigationCallback);
- if (isSupported(Constants.STATISTICS)) {
- prox.requestLastTrip(config, lastTripCallback);
- prox.requestAllTrips(config, allTripsCallback);
- addCallback(lastTripCallback);
- addCallback(allTripsCallback);
- }
- if (isSupported(Constants.LAST_DESTINATIONS)) {
- prox.requestDestinations(config, destinationCallback);
- addCallback(destinationCallback);
- }
- if (isElectric) {
- prox.requestChargingProfile(config, chargeProfileCallback);
- addCallback(chargeProfileCallback);
- }
- synchronized (imageProperties) {
- if (!imageCache.isPresent() && !imageProperties.failLimitReached()) {
- prox.requestImage(config, imageProperties, imageCallback);
- addCallback(imageCallback);
- }
- }
- }, () -> {
- logger.warn("ConnectedDrive Configuration isn't present");
- });
- }, () -> {
- logger.warn("ConnectedDrive Proxy isn't present");
- });
- }
-
- private synchronized void addCallback(ResponseCallback rc) {
- callbackCounter.ifPresent(counter -> counter.add(rc));
- }
-
- private synchronized void removeCallback(ResponseCallback rc) {
- callbackCounter.ifPresent(counter -> {
- counter.remove(rc);
- // all necessary callbacks received => print and set to empty
- if (counter.isEmpty()) {
- logFingerPrint();
- callbackCounter = Optional.empty();
- }
- });
- }
-
- private void logFingerPrint() {
- final String vin = configuration.map(config -> config.vin).orElse("");
- logger.debug("###### Vehicle Troubleshoot Fingerprint Data - BEGIN ######");
- logger.debug("### Discovery Result ###");
- bridgeHandler.ifPresent(handler -> {
- logger.debug("{}", handler.getDiscoveryFingerprint());
- });
- vehicleStatusCache.ifPresentOrElse(vehicleStatus -> {
- logger.debug("### Vehicle Status ###");
-
- // Anonymous data for VIN and Position
- try {
- VehicleStatusContainer container = Converter.getGson().fromJson(vehicleStatus,
- VehicleStatusContainer.class);
- if (container != null) {
- VehicleStatus status = container.vehicleStatus;
- if (status != null) {
- status.vin = Constants.ANONYMOUS;
- if (status.position != null) {
- status.position.lat = -1;
- status.position.lon = -1;
- status.position.heading = -1;
- }
- }
- }
- logger.debug("{}", Converter.getGson().toJson(container));
- } catch (JsonSyntaxException jse) {
- logger.debug("{}", jse.getMessage());
- }
- }, () -> {
- logger.debug("### Vehicle Status Empty ###");
- });
- lastTripCache.ifPresentOrElse(lastTrip -> {
- logger.debug("### Last Trip ###");
- logger.debug("{}", lastTrip.replaceAll(vin, Constants.ANONYMOUS));
- }, () -> {
- logger.debug("### Last Trip Empty ###");
- });
- allTripsCache.ifPresentOrElse(allTrips -> {
- logger.debug("### All Trips ###");
- logger.debug("{}", allTrips.replaceAll(vin, Constants.ANONYMOUS));
- }, () -> {
- logger.debug("### All Trips Empty ###");
- });
- if (isElectric) {
- chargeProfileCache.ifPresentOrElse(chargeProfile -> {
- logger.debug("### Charge Profile ###");
- logger.debug("{}", chargeProfile.replaceAll(vin, Constants.ANONYMOUS));
- }, () -> {
- logger.debug("### Charge Profile Empty ###");
- });
- }
- destinationCache.ifPresentOrElse(destination -> {
- logger.debug("### Charge Profile ###");
- try {
- DestinationContainer container = Converter.getGson().fromJson(destination, DestinationContainer.class);
- if (container != null) {
- if (container.destinations != null) {
- container.destinations.forEach(entry -> {
- entry.lat = 0;
- entry.lon = 0;
- entry.city = Constants.ANONYMOUS;
- entry.street = Constants.ANONYMOUS;
- entry.streetNumber = Constants.ANONYMOUS;
- entry.country = Constants.ANONYMOUS;
- });
- logger.debug("{}", Converter.getGson().toJson(container));
- }
- } else {
- logger.debug("### Destinations Empty ###");
- }
- } catch (JsonSyntaxException jse) {
- logger.debug("{}", jse.getMessage());
- }
- }, () -> {
- logger.debug("### Charge Profile Empty ###");
- });
- rangeMapCache.ifPresentOrElse(rangeMap -> {
- logger.debug("### Range Map ###");
- logger.debug("{}", rangeMap.replaceAll(vin, Constants.ANONYMOUS));
- }, () -> {
- logger.debug("### Range Map Empty ###");
- });
- logger.debug("###### Vehicle Troubleshoot Fingerprint Data - END ######");
- }
-
- /**
- * Don't stress ConnectedDrive with unnecessary requests. One call at the beginning is done to check the response.
- * After cache has e.g. a proper error response it will be shown in the fingerprint
- *
- * @return
- */
- private boolean isSupported(String service) {
- final String services = thing.getProperties().get(Constants.SERVICES_SUPPORTED);
- if (services != null) {
- if (services.contains(service)) {
- return true;
- }
- }
- // if cache is empty give it a try one time to collected Troubleshoot data
- return lastTripCache.isEmpty() || allTripsCache.isEmpty() || destinationCache.isEmpty();
- }
-
- public void updateRemoteExecutionStatus(@Nullable String service, @Nullable String status) {
- if (RemoteService.CHARGING_CONTROL.toString().equals(service)
- && ExecutionState.EXECUTED.name().equals(status)) {
- saveChargeProfileSent();
- }
- updateChannel(CHANNEL_GROUP_REMOTE, REMOTE_STATE, StringType
- .valueOf(Converter.toTitleCase((service == null ? "-" : service) + Constants.SPACE + status)));
- }
-
- public Optional<VehicleConfiguration> getConfiguration() {
- return configuration;
- }
-
- public ScheduledExecutorService getScheduler() {
- return scheduler;
- }
-
- /**
- * Callbacks for ConnectedDrive Portal
- *
- * @author Bernd Weymann
- *
- */
- public class ChargeProfilesCallback implements StringResponseCallback {
- @Override
- public void onResponse(@Nullable String content) {
- if (content != null) {
- chargeProfileCache = Optional.of(content);
- if (chargeProfileEdit.isEmpty()) {
- updateChargeProfileFromContent(content);
- }
- }
- removeCallback(this);
- }
-
- /**
- * Store Error Report in cache variable. Via Fingerprint Channel error is logged and Issue can be raised
- */
- @Override
- public void onError(NetworkError error) {
- logger.debug("{}", error.toString());
- chargeProfileCache = Optional.of(Converter.getGson().toJson(error));
- removeCallback(this);
- }
- }
-
- public class RangeMapCallback implements StringResponseCallback {
- @Override
- public void onResponse(@Nullable String content) {
- rangeMapCache = Optional.ofNullable(content);
- removeCallback(this);
- }
-
- /**
- * Store Error Report in cache variable. Via Fingerprint Channel error is logged and Issue can be raised
- */
- @Override
- public void onError(NetworkError error) {
- logger.debug("{}", error.toString());
- rangeMapCache = Optional.of(Converter.getGson().toJson(error));
- removeCallback(this);
- }
- }
-
- public class DestinationsCallback implements StringResponseCallback {
-
- @Override
- public void onResponse(@Nullable String content) {
- destinationCache = Optional.ofNullable(content);
- if (content != null) {
- try {
- DestinationContainer dc = Converter.getGson().fromJson(content, DestinationContainer.class);
- if (dc != null && dc.destinations != null) {
- updateDestinations(dc.destinations);
- }
- } catch (JsonSyntaxException jse) {
- logger.debug("{}", jse.getMessage());
- }
- }
- removeCallback(this);
- }
-
- /**
- * Store Error Report in cache variable. Via Fingerprint Channel error is logged and Issue can be raised
- */
- @Override
- public void onError(NetworkError error) {
- logger.debug("{}", error.toString());
- destinationCache = Optional.of(Converter.getGson().toJson(error));
- removeCallback(this);
- }
- }
-
- public class ImageCallback implements ByteResponseCallback {
- @Override
- public void onResponse(byte[] content) {
- if (content.length > 0) {
- imageCache = Optional.of(content);
- String contentType = HttpUtil.guessContentTypeFromData(content);
- updateChannel(CHANNEL_GROUP_VEHICLE_IMAGE, IMAGE_FORMAT, new RawType(content, contentType));
- } else {
- synchronized (imageProperties) {
- imageProperties.failed();
- }
- }
- removeCallback(this);
- }
-
- /**
- * Store Error Report in cache variable. Via Fingerprint Channel error is logged and Issue can be raised
- */
- @Override
- public void onError(NetworkError error) {
- logger.debug("{}", error.toString());
- synchronized (imageProperties) {
- imageProperties.failed();
- }
- removeCallback(this);
- }
- }
-
- public class AllTripsCallback implements StringResponseCallback {
- @Override
- public void onResponse(@Nullable String content) {
- if (content != null) {
- allTripsCache = Optional.of(content);
- try {
- AllTripsContainer atc = Converter.getGson().fromJson(content, AllTripsContainer.class);
- if (atc != null) {
- AllTrips at = atc.allTrips;
- if (at != null) {
- updateAllTrips(at);
- }
- }
- } catch (JsonSyntaxException jse) {
- logger.debug("{}", jse.getMessage());
- }
- }
- removeCallback(this);
- }
-
- /**
- * Store Error Report in cache variable. Via Fingerprint Channel error is logged and Issue can be raised
- */
- @Override
- public void onError(NetworkError error) {
- logger.debug("{}", error.toString());
- allTripsCache = Optional.of(Converter.getGson().toJson(error));
- removeCallback(this);
- }
- }
-
- public class LastTripCallback implements StringResponseCallback {
- @Override
- public void onResponse(@Nullable String content) {
- if (content != null) {
- lastTripCache = Optional.of(content);
- try {
- LastTripContainer lt = Converter.getGson().fromJson(content, LastTripContainer.class);
- if (lt != null) {
- LastTrip trip = lt.lastTrip;
- if (trip != null) {
- updateLastTrip(trip);
- }
- }
- } catch (JsonSyntaxException jse) {
- logger.debug("{}", jse.getMessage());
- }
- }
- removeCallback(this);
- }
-
- /**
- * Store Error Report in cache variable. Via Fingerprint Channel error is logged and Issue can be raised
- */
- @Override
- public void onError(NetworkError error) {
- logger.debug("{}", error.toString());
- lastTripCache = Optional.of(Converter.getGson().toJson(error));
- removeCallback(this);
- }
- }
-
- /**
- * The VehicleStatus is supported by all Vehicle Types so it's used to reflect the Thing Status
- */
- public class VehicleStatusCallback implements StringResponseCallback {
- @Override
- public void onResponse(@Nullable String content) {
- if (content != null) {
- // switch to non legacy mode
- legacyMode = 0;
- updateStatus(ThingStatus.ONLINE);
- vehicleStatusCache = Optional.of(content);
- try {
- VehicleStatusContainer status = Converter.getGson().fromJson(content, VehicleStatusContainer.class);
- if (status != null) {
- VehicleStatus vStatus = status.vehicleStatus;
- if (vStatus == null) {
- return;
- }
- updateVehicleStatus(vStatus);
- updateCheckControls(vStatus.checkControlMessages);
- updateServices(vStatus.cbsData);
- updatePosition(vStatus.position);
- }
- } catch (JsonSyntaxException jse) {
- logger.debug("{}", jse.getMessage());
- }
- }
- removeCallback(this);
- }
-
- @Override
- public void onError(NetworkError error) {
- logger.debug("{}", error.toString());
- // only if legacyMode isn't set yet try legacy API
- if (error.status != 200 && legacyMode == Constants.INT_UNDEF) {
- logger.debug("VehicleStatus not found - try legacy API");
- proxy.get().requestLegacyVehcileStatus(configuration.get(), oldVehicleStatusCallback);
- }
- vehicleStatusCache = Optional.of(Converter.getGson().toJson(error));
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, error.reason);
- removeCallback(this);
- }
- }
-
- /**
- * Fallback API if origin isn't supported.
- * This comes from the Community Discussion where a Vehicle from 2015 answered with "404"
- * https://community.openhab.org/t/bmw-connecteddrive-binding/105124
- *
- * Selection of API was discussed here
- * https://community.openhab.org/t/bmw-connecteddrive-bmw-i3/103876
- *
- * I figured out that only one API was working for this Vehicle. So this backward compatible Callback is introduced.
- * The delivered data is converted into the origin dto object so no changes in previous functional code needed
- */
- public class LegacyVehicleStatusCallback implements StringResponseCallback {
- @Override
- public void onResponse(@Nullable String content) {
- if (content != null) {
- try {
- VehicleAttributesContainer vac = Converter.getGson().fromJson(content,
- VehicleAttributesContainer.class);
- vehicleStatusCallback.onResponse(Converter.transformLegacyStatus(vac));
- legacyMode = 1;
- logger.debug("VehicleStatus switched to legacy mode");
- } catch (JsonSyntaxException jse) {
- logger.debug("{}", jse.getMessage());
- }
- }
- }
-
- @Override
- public void onError(NetworkError error) {
- vehicleStatusCallback.onError(error);
- }
- }
-
- public class NavigationStatusCallback implements StringResponseCallback {
- @Override
- public void onResponse(@Nullable String content) {
- if (content != null) {
- try {
- NavigationContainer nav = Converter.getGson().fromJson(content, NavigationContainer.class);
- updateChannel(CHANNEL_GROUP_RANGE, SOC_MAX, QuantityType.valueOf(nav.socmax, Units.KILOWATT_HOUR));
- } catch (JsonSyntaxException jse) {
- logger.debug("{}", jse.getMessage());
- }
- }
- removeCallback(this);
- }
-
- @Override
- public void onError(NetworkError error) {
- logger.debug("{}", error.toString());
- removeCallback(this);
- }
- }
-
- private void handleChargeProfileCommand(ChannelUID channelUID, Command command) {
- if (chargeProfileEdit.isEmpty()) {
- chargeProfileEdit = getChargeProfileWrapper();
- }
-
- chargeProfileEdit.ifPresent(profile -> {
-
- boolean processed = false;
-
- final String id = channelUID.getIdWithoutGroup();
-
- if (command instanceof StringType) {
- final String stringCommand = ((StringType) command).toFullString();
- switch (id) {
- case CHARGE_PROFILE_PREFERENCE:
- profile.setPreference(stringCommand);
- updateChannel(CHANNEL_GROUP_CHARGE, CHARGE_PROFILE_PREFERENCE,
- StringType.valueOf(Converter.toTitleCase(profile.getPreference())));
- processed = true;
- break;
- case CHARGE_PROFILE_MODE:
- profile.setMode(stringCommand);
- updateChannel(CHANNEL_GROUP_CHARGE, CHARGE_PROFILE_MODE,
- StringType.valueOf(Converter.toTitleCase(profile.getMode())));
- processed = true;
- break;
- default:
- break;
- }
- } else if (command instanceof OnOffType) {
- final ProfileKey enableKey = ChargeProfileUtils.getEnableKey(id);
- if (enableKey != null) {
- profile.setEnabled(enableKey, OnOffType.ON.equals(command));
- updateTimedState(profile, enableKey);
- processed = true;
- } else {
- final ChargeKeyDay chargeKeyDay = ChargeProfileUtils.getKeyDay(id);
- if (chargeKeyDay != null) {
- profile.setDayEnabled(chargeKeyDay.key, chargeKeyDay.day, OnOffType.ON.equals(command));
- updateTimedState(profile, chargeKeyDay.key);
- processed = true;
- }
- }
- } else if (command instanceof DateTimeType) {
- DateTimeType dtt = (DateTimeType) command;
- logger.debug("Accept {} for ID {}", dtt.toFullString(), id);
- final ProfileKey key = ChargeProfileUtils.getTimeKey(id);
- if (key != null) {
- profile.setTime(key, dtt.getZonedDateTime().toLocalTime());
- updateTimedState(profile, key);
- processed = true;
- }
- }
-
- if (processed) {
- // cancel current timer and add another 5 mins - valid for each edit
- editTimeout.ifPresent(timeout -> timeout.cancel(true));
- // start edit timer with 5 min timeout
- editTimeout = Optional.of(scheduler.schedule(() -> {
- editTimeout = Optional.empty();
- chargeProfileEdit = Optional.empty();
- chargeProfileCache.ifPresent(this::updateChargeProfileFromContent);
- }, 5, TimeUnit.MINUTES));
- } else {
- logger.debug("unexpected command {} not processed", command.toFullString());
- }
- });
- }
-
- private void saveChargeProfileSent() {
- editTimeout.ifPresent(timeout -> {
- timeout.cancel(true);
- editTimeout = Optional.empty();
- });
- chargeProfileSent.ifPresent(sent -> {
- chargeProfileCache = Optional.of(sent);
- chargeProfileSent = Optional.empty();
- chargeProfileEdit = Optional.empty();
- chargeProfileCache.ifPresent(this::updateChargeProfileFromContent);
- });
- }
-
- @Override
- public Collection<Class<? extends ThingHandlerService>> getServices() {
- return Set.of(BMWConnectedDriveActions.class);
- }
-
- public Optional<ChargeProfileWrapper> getChargeProfileWrapper() {
- return chargeProfileCache.flatMap(cache -> {
- return ChargeProfileWrapper.fromJson(cache).map(wrapper -> {
- return wrapper;
- }).or(() -> {
- logger.debug("cannot parse charging profile: {}", cache);
- return Optional.empty();
- });
- }).or(() -> {
- logger.debug("No ChargeProfile recieved so far - cannot start editing");
- return Optional.empty();
- });
- }
-
- public void sendChargeProfile(Optional<ChargeProfileWrapper> profile) {
- profile.map(profil -> profil.getJson()).ifPresent(json -> {
- logger.debug("sending charging profile: {}", json);
- chargeProfileSent = Optional.of(json);
- remote.ifPresent(rem -> rem.execute(RemoteService.CHARGING_CONTROL, json));
- });
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler.simulation;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link Injector} Simulates feedback of the ConnectedDrive Portal
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class Injector {
- private static boolean active = false;
-
- // copy discovery json here
- private static String discovery = "";
-
- // copy vehicle status json here
- private static String status = "";
-
- public static boolean isActive() {
- return active;
- }
-
- public static String getDiscovery() {
- return discovery;
- }
-
- public static String getStatus() {
- return status;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.utils;
-
-import java.util.Map;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link BimmerConstants} This class holds the important constants for the BMW Connected Drive Authorization. They
- * are taken from the Bimmercode from github {@link https://github.com/bimmerconnected/bimmer_connected}
- * File defining these constants
- * {@link https://github.com/bimmerconnected/bimmer_connected/blob/master/bimmer_connected/account.py}
- * https://customer.bmwgroup.com/one/app/oauth.js
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class BimmerConstants {
-
- // https://github.com/bimmerconnected/bimmer_connected/blob/master/bimmer_connected/country_selector.py
- public static final String REGION_NORTH_AMERICA = "NORTH_AMERICA";
- public static final String REGION_CHINA = "CHINA";
- public static final String REGION_ROW = "ROW";
-
- // https://github.com/bimmerconnected/bimmer_connected/blob/master/bimmer_connected/country_selector.py
- public static final String LEGACY_AUTH_SERVER_NORTH_AMERICA = "login.bmwusa.com/gcdm";
- public static final String LEGACY_AUTH_SERVER_CHINA = "customer.bmwgroup.cn/gcdm";
- public static final String LEGACY_AUTH_SERVER_ROW = "customer.bmwgroup.com/gcdm";
- public static final Map<String, String> LEGACY_AUTH_SERVER_MAP = Map.of(REGION_NORTH_AMERICA,
- LEGACY_AUTH_SERVER_NORTH_AMERICA, REGION_CHINA, LEGACY_AUTH_SERVER_CHINA, REGION_ROW,
- LEGACY_AUTH_SERVER_ROW);
-
- public static final String OAUTH_ENDPOINT = "/oauth/authenticate";
- public static final String TOKEN_ENDPOINT = "/oauth/token";
-
- public static final String API_SERVER_NORTH_AMERICA = "b2vapi.bmwgroup.us";
- public static final String API_SERVER_CHINA = "b2vapi.bmwgroup.cn:8592";
- public static final String API_SERVER_ROW = "b2vapi.bmwgroup.com";
-
- public static final String EADRAX_SERVER_NORTH_AMERICA = "cocoapi.bmwgroup.us";
- public static final String EADRAX_SERVER_ROW = "cocoapi.bmwgroup.com";
- public static final String EADRAX_SERVER_CHINA = Constants.EMPTY;
- public static final Map<String, String> EADRAX_SERVER_MAP = Map.of(REGION_NORTH_AMERICA,
- EADRAX_SERVER_NORTH_AMERICA, REGION_CHINA, EADRAX_SERVER_CHINA, REGION_ROW, EADRAX_SERVER_ROW);
-
- public static final Map<String, String> API_SERVER_MAP = Map.of(REGION_NORTH_AMERICA, API_SERVER_NORTH_AMERICA,
- REGION_CHINA, API_SERVER_CHINA, REGION_ROW, API_SERVER_ROW);
-
- // see https://github.com/bimmerconnected/bimmer_connected/pull/252/files
- public static final Map<String, String> LEGACY_AUTHORIZATION_VALUE_MAP = Map.of(REGION_NORTH_AMERICA,
- "Basic ZDc2NmI1MzctYTY1NC00Y2JkLWEzZGMtMGNhNTY3MmQ3ZjhkOjE1ZjY5N2Y2LWE1ZDUtNGNhZC05OWQ5LTNhMTViYzdmMzk3Mw==",
- REGION_CHINA,
- "Basic blF2NkNxdHhKdVhXUDc0eGYzQ0p3VUVQOjF6REh4NnVuNGNEanliTEVOTjNreWZ1bVgya0VZaWdXUGNRcGR2RFJwSUJrN3JPSg==",
- REGION_ROW,
- "Basic ZDc2NmI1MzctYTY1NC00Y2JkLWEzZGMtMGNhNTY3MmQ3ZjhkOjE1ZjY5N2Y2LWE1ZDUtNGNhZC05OWQ5LTNhMTViYzdmMzk3Mw==");
-
- public static final String LEGACY_CREDENTIAL_VALUES = "nQv6CqtxJuXWP74xf3CJwUEP:1zDHx6un4cDjybLENN3kyfumX2kEYigWPcQpdvDRpIBk7rOJ";
- public static final String LEGACY_REDIRECT_URI_VALUE = "https://www.bmw-connecteddrive.com/app/static/external-dispatch.html";
- public static final String LEGACY_SCOPE_VALUES = "authenticate_user vehicle_data remote_services";
- public static final String LEGACY_CLIENT_ID = "dbf0a542-ebd1-4ff0-a9a7-55172fbfce35";
-
- public static final String LEGACY_REFERER_URL = "https://www.bmw-connecteddrive.de/app/index.html";
-
- public static final String AUTH_SERVER_NORTH_AMERICA = "login.bmwusa.com/gcdm";
- public static final String AUTH_SERVER_CHINA = "customer.bmwgroup.cn/gcdm";
- public static final String AUTH_SERVER_ROW = "customer.bmwgroup.com/gcdm";
- public static final Map<String, String> AUTH_SERVER_MAP = Map.of(REGION_NORTH_AMERICA, AUTH_SERVER_NORTH_AMERICA,
- REGION_CHINA, AUTH_SERVER_CHINA, REGION_ROW, AUTH_SERVER_ROW);
-
- public static final Map<String, String> AUTHORIZATION_VALUE_MAP = Map.of(REGION_NORTH_AMERICA,
- "Basic NTQzOTRhNGItYjZjMS00NWZlLWI3YjItOGZkM2FhOTI1M2FhOmQ5MmYzMWMwLWY1NzktNDRmNS1hNzdkLTk2NmY4ZjAwZTM1MQ==",
- REGION_CHINA,
- "Basic blF2NkNxdHhKdVhXUDc0eGYzQ0p3VUVQOjF6REh4NnVuNGNEanliTEVOTjNreWZ1bVgya0VZaWdXUGNRcGR2RFJwSUJrN3JPSg==",
- REGION_ROW,
- "Basic MzFjMzU3YTAtN2ExZC00NTkwLWFhOTktMzNiOTcyNDRkMDQ4OmMwZTMzOTNkLTcwYTItNGY2Zi05ZDNjLTg1MzBhZjY0ZDU1Mg==");
-
- public static final Map<String, String> CODE_VERIFIER = Map.of(REGION_NORTH_AMERICA,
- "BKDarcVUpgymBDCgHDH0PwwMfzycDxu1joeklioOhwXA", REGION_CHINA, Constants.EMPTY, REGION_ROW,
- "7PsmfPS5MpaNt0jEcPpi-B7M7u0gs1Nzw6ex0Y9pa-0");
-
- public static final Map<String, String> CLIENT_ID = Map.of(REGION_NORTH_AMERICA,
- "54394a4b-b6c1-45fe-b7b2-8fd3aa9253aa", REGION_CHINA, Constants.EMPTY, REGION_ROW,
- "31c357a0-7a1d-4590-aa99-33b97244d048");
-
- public static final Map<String, String> STATE = Map.of(REGION_NORTH_AMERICA, "rgastJbZsMtup49-Lp0FMQ", REGION_CHINA,
- Constants.EMPTY, REGION_ROW, "cEG9eLAIi6Nv-aaCAniziE_B6FPoobva3qr5gukilYw");
-
- public static final String REDIRECT_URI_VALUE = "com.bmw.connected://oauth";
- public static final String SCOPE_VALUES = "openid profile email offline_access smacc vehicle_data perseus dlm svds cesim vsapi remote_services fupo authenticate_user";
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.utils;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.*;
-
-import java.time.DayOfWeek;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileWrapper.ProfileKey;
-
-/**
- * The {@link ChargeProfileUtils} utility functions for charging profiles
- *
- * @author Norbert Truchsess - initial contribution
- */
-@NonNullByDefault
-public class ChargeProfileUtils {
-
- // Charging
- public static class TimedChannel {
- public final String time;
- public final @Nullable String timer;
- public final boolean hasDays;
-
- TimedChannel(final String time, @Nullable final String timer, final boolean hasDays) {
- this.time = time;
- this.timer = timer;
- this.hasDays = hasDays;
- }
- }
-
- @SuppressWarnings("serial")
- private static final Map<ProfileKey, TimedChannel> TIMED_CHANNELS = new HashMap<>() {
- {
- put(ProfileKey.WINDOWSTART, new TimedChannel(CHARGE_WINDOW_START, null, false));
- put(ProfileKey.WINDOWEND, new TimedChannel(CHARGE_WINDOW_END, null, false));
- put(ProfileKey.TIMER1, new TimedChannel(CHARGE_TIMER1 + CHARGE_DEPARTURE, CHARGE_TIMER1, true));
- put(ProfileKey.TIMER2, new TimedChannel(CHARGE_TIMER2 + CHARGE_DEPARTURE, CHARGE_TIMER2, true));
- put(ProfileKey.TIMER3, new TimedChannel(CHARGE_TIMER3 + CHARGE_DEPARTURE, CHARGE_TIMER3, true));
- put(ProfileKey.OVERRIDE, new TimedChannel(CHARGE_OVERRIDE + CHARGE_DEPARTURE, CHARGE_OVERRIDE, false));
- }
- };
-
- @SuppressWarnings("serial")
- private static final Map<DayOfWeek, String> DAY_CHANNELS = new HashMap<>() {
- {
- put(DayOfWeek.MONDAY, CHARGE_DAY_MON);
- put(DayOfWeek.TUESDAY, CHARGE_DAY_TUE);
- put(DayOfWeek.WEDNESDAY, CHARGE_DAY_WED);
- put(DayOfWeek.THURSDAY, CHARGE_DAY_THU);
- put(DayOfWeek.FRIDAY, CHARGE_DAY_FRI);
- put(DayOfWeek.SATURDAY, CHARGE_DAY_SAT);
- put(DayOfWeek.SUNDAY, CHARGE_DAY_SUN);
- }
- };
-
- public static class ChargeKeyDay {
- public final ProfileKey key;
- public final DayOfWeek day;
-
- ChargeKeyDay(final ProfileKey key, final DayOfWeek day) {
- this.key = key;
- this.day = day;
- }
- }
-
- @SuppressWarnings("serial")
- private static final Map<String, ProfileKey> CHARGE_ENABLED_CHANNEL_KEYS = new HashMap<>() {
- {
- TIMED_CHANNELS.forEach((key, channel) -> {
- put(channel.timer + CHARGE_ENABLED, key);
- });
- put(CHARGE_PROFILE_CLIMATE, ProfileKey.CLIMATE);
- }
- };
-
- @SuppressWarnings("serial")
- private static final Map<String, ProfileKey> CHARGE_TIME_CHANNEL_KEYS = new HashMap<>() {
- {
- TIMED_CHANNELS.forEach((key, channel) -> {
- put(channel.time, key);
- });
- }
- };
-
- @SuppressWarnings("serial")
- private static final Map<String, ChargeKeyDay> CHARGE_DAYS_CHANNEL_KEYS = new HashMap<>() {
- {
- DAY_CHANNELS.forEach((dayOfWeek, dayChannel) -> {
- put(CHARGE_TIMER1 + dayChannel, new ChargeKeyDay(ProfileKey.TIMER1, dayOfWeek));
- put(CHARGE_TIMER2 + dayChannel, new ChargeKeyDay(ProfileKey.TIMER2, dayOfWeek));
- put(CHARGE_TIMER3 + dayChannel, new ChargeKeyDay(ProfileKey.TIMER3, dayOfWeek));
- });
- }
- };
-
- public static @Nullable TimedChannel getTimedChannel(ProfileKey key) {
- return TIMED_CHANNELS.get(key);
- }
-
- public static @Nullable String getDaysChannel(DayOfWeek day) {
- return DAY_CHANNELS.get(day);
- }
-
- public static @Nullable ProfileKey getEnableKey(final String id) {
- return CHARGE_ENABLED_CHANNEL_KEYS.get(id);
- }
-
- public static @Nullable ChargeKeyDay getKeyDay(final String id) {
- return CHARGE_DAYS_CHANNEL_KEYS.get(id);
- }
-
- public static @Nullable ProfileKey getTimeKey(final String id) {
- return CHARGE_TIME_CHANNEL_KEYS.get(id);
- }
-
- public static String formatDays(final Set<DayOfWeek> weekdays) {
- return weekdays.stream().map(day -> Constants.DAYS.get(day)).collect(Collectors.joining(Constants.COMMA));
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.utils;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileWrapper.ProfileKey.*;
-import static org.openhab.binding.bmwconnecteddrive.internal.utils.Constants.*;
-
-import java.time.DayOfWeek;
-import java.time.LocalTime;
-import java.time.format.DateTimeParseException;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.ChargingMode;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.ChargingPreference;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.charge.ChargeProfile;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.charge.ChargingWindow;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.charge.Timer;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.charge.WeeklyPlanner;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.JsonSyntaxException;
-
-/**
- * The {@link ChargeProfileWrapper} Wrapper for ChargeProfiles
- *
- * @author Bernd Weymann - Initial contribution
- * @author Norbert Truchsess - add ChargeProfileActions
- */
-@NonNullByDefault
-public class ChargeProfileWrapper {
- private static final Logger LOGGER = LoggerFactory.getLogger(ChargeProfileWrapper.class);
-
- public enum ProfileType {
- WEEKLY,
- TWO_TIMES,
- EMPTY
- }
-
- public enum ProfileKey {
- CLIMATE,
- TIMER1,
- TIMER2,
- TIMER3,
- TIMER4,
- OVERRIDE,
- WINDOWSTART,
- WINDOWEND
- }
-
- protected final ProfileType type;
-
- private Optional<ChargingMode> mode = Optional.empty();
- private Optional<ChargingPreference> preference = Optional.empty();
-
- private final Map<ProfileKey, Boolean> enabled = new HashMap<>();
- private final Map<ProfileKey, LocalTime> times = new HashMap<>();
- private final Map<ProfileKey, Set<DayOfWeek>> daysOfWeek = new HashMap<>();
-
- public static Optional<ChargeProfileWrapper> fromJson(final String content) {
- try {
- final ChargeProfile cp = Converter.getGson().fromJson(content, ChargeProfile.class);
- if (cp != null) {
- return Optional.of(new ChargeProfileWrapper(cp));
- }
- } catch (JsonSyntaxException jse) {
- LOGGER.debug("ChargeProfile unparsable: {}", content);
- }
- return Optional.empty();
- }
-
- private ChargeProfileWrapper(final ChargeProfile profile) {
- final WeeklyPlanner planner;
-
- if (profile.weeklyPlanner != null) {
- type = ProfileType.WEEKLY;
- planner = profile.weeklyPlanner;
- } else if (profile.twoTimesTimer != null) {
- type = ProfileType.TWO_TIMES;
- planner = profile.twoTimesTimer;
- // timer days not supported
- } else {
- type = ProfileType.EMPTY;
- return;
- }
-
- setPreference(planner.chargingPreferences);
- setMode(planner.chargingMode);
-
- setEnabled(CLIMATE, planner.climatizationEnabled);
-
- addTimer(TIMER1, planner.timer1);
- addTimer(TIMER2, planner.timer2);
-
- if (planner.preferredChargingWindow != null) {
- addTime(WINDOWSTART, planner.preferredChargingWindow.startTime);
- addTime(WINDOWEND, planner.preferredChargingWindow.endTime);
- } else {
- preference.ifPresent(pref -> {
- if (ChargingPreference.CHARGING_WINDOW.equals(pref)) {
- addTime(WINDOWSTART, null);
- addTime(WINDOWEND, null);
- }
- });
- }
-
- if (isWeekly()) {
- addTimer(TIMER3, planner.timer3);
- addTimer(OVERRIDE, planner.overrideTimer);
- }
- }
-
- public @Nullable Boolean isEnabled(final ProfileKey key) {
- return enabled.get(key);
- }
-
- public void setEnabled(final ProfileKey key, @Nullable final Boolean enabled) {
- if (enabled == null) {
- this.enabled.remove(key);
- } else {
- this.enabled.put(key, enabled);
- }
- }
-
- public @Nullable String getMode() {
- return mode.map(m -> m.name()).orElse(null);
- }
-
- public void setMode(final @Nullable String mode) {
- if (mode != null) {
- try {
- this.mode = Optional.of(ChargingMode.valueOf(mode));
- return;
- } catch (IllegalArgumentException iae) {
- LOGGER.warn("unexpected value for chargingMode: {}", mode);
- }
- }
- this.mode = Optional.empty();
- }
-
- public @Nullable String getPreference() {
- return preference.map(pref -> pref.name()).orElse(null);
- }
-
- public void setPreference(final @Nullable String preference) {
- if (preference != null) {
- try {
- this.preference = Optional.of(ChargingPreference.valueOf(preference));
- return;
- } catch (IllegalArgumentException iae) {
- LOGGER.warn("unexpected value for chargingPreference: {}", preference);
- }
- }
- this.preference = Optional.empty();
- }
-
- public @Nullable Set<DayOfWeek> getDays(final ProfileKey key) {
- return daysOfWeek.get(key);
- }
-
- public void setDays(final ProfileKey key, final @Nullable Set<DayOfWeek> days) {
- if (days == null) {
- daysOfWeek.remove(key);
- } else {
- daysOfWeek.put(key, days);
- }
- }
-
- public void setDayEnabled(final ProfileKey key, final DayOfWeek day, final boolean enabled) {
- final Set<DayOfWeek> days = daysOfWeek.get(key);
- if (days == null) {
- daysOfWeek.put(key, enabled ? EnumSet.of(day) : EnumSet.noneOf(DayOfWeek.class));
- } else {
- if (enabled) {
- days.add(day);
- } else {
- days.remove(day);
- }
- }
- }
-
- public @Nullable LocalTime getTime(final ProfileKey key) {
- return times.get(key);
- }
-
- public void setTime(final ProfileKey key, @Nullable LocalTime time) {
- if (time == null) {
- times.remove(key);
- } else {
- times.put(key, time);
- }
- }
-
- public String getJson() {
- final ChargeProfile profile = new ChargeProfile();
- final WeeklyPlanner planner = new WeeklyPlanner();
-
- preference.ifPresent(pref -> planner.chargingPreferences = pref.name());
- planner.climatizationEnabled = isEnabled(CLIMATE);
- preference.ifPresent(pref -> {
- if (ChargingPreference.CHARGING_WINDOW.equals(pref)) {
- planner.chargingMode = getMode();
- final LocalTime start = getTime(WINDOWSTART);
- final LocalTime end = getTime(WINDOWEND);
- if (start != null || end != null) {
- planner.preferredChargingWindow = new ChargingWindow();
- planner.preferredChargingWindow.startTime = start == null ? null : start.format(TIME_FORMATER);
- planner.preferredChargingWindow.endTime = end == null ? null : end.format(TIME_FORMATER);
- }
- }
- });
- planner.timer1 = getTimer(TIMER1);
- planner.timer2 = getTimer(TIMER2);
- if (isWeekly()) {
- planner.timer3 = getTimer(TIMER3);
- planner.overrideTimer = getTimer(OVERRIDE);
- profile.weeklyPlanner = planner;
- } else if (isTwoTimes()) {
- profile.twoTimesTimer = planner;
- }
- return Converter.getGson().toJson(profile);
- }
-
- private void addTime(final ProfileKey key, @Nullable final String time) {
- try {
- times.put(key, time == null ? NULL_LOCAL_TIME : LocalTime.parse(time, TIME_FORMATER));
- } catch (DateTimeParseException dtpe) {
- LOGGER.warn("unexpected value for {} time: {}", key.name(), time);
- }
- }
-
- private void addTimer(final ProfileKey key, @Nullable final Timer timer) {
- if (timer == null) {
- enabled.put(key, false);
- addTime(key, null);
- if (isWeekly()) {
- daysOfWeek.put(key, EnumSet.noneOf(DayOfWeek.class));
- }
- } else {
- enabled.put(key, timer.timerEnabled);
- addTime(key, timer.departureTime);
- if (isWeekly()) {
- final EnumSet<DayOfWeek> daySet = EnumSet.noneOf(DayOfWeek.class);
- if (timer.weekdays != null) {
- for (String day : timer.weekdays) {
- try {
- daySet.add(DayOfWeek.valueOf(day));
- } catch (IllegalArgumentException iae) {
- LOGGER.warn("unexpected value for {} day: {}", key.name(), day);
- }
- }
- }
- daysOfWeek.put(key, daySet);
- }
- }
- }
-
- private @Nullable Timer getTimer(final ProfileKey key) {
- final Timer timer = new Timer();
- timer.timerEnabled = enabled.get(key);
- final LocalTime time = times.get(key);
- timer.departureTime = time == null ? null : time.format(TIME_FORMATER);
- if (isWeekly()) {
- final Set<DayOfWeek> days = daysOfWeek.get(key);
- if (days != null) {
- timer.weekdays = new ArrayList<>();
- for (DayOfWeek day : days) {
- timer.weekdays.add(day.name());
- }
- }
- }
- return timer.timerEnabled == null && timer.departureTime == null && timer.weekdays == null ? null : timer;
- }
-
- private boolean isWeekly() {
- return ProfileType.WEEKLY.equals(type);
- }
-
- private boolean isTwoTimes() {
- return ProfileType.TWO_TIMES.equals(type);
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.utils;
-
-import java.time.DayOfWeek;
-import java.time.LocalDate;
-import java.time.LocalTime;
-import java.time.format.DateTimeFormatter;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.measure.Unit;
-import javax.measure.quantity.Length;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.library.unit.MetricPrefix;
-import org.openhab.core.library.unit.SIUnits;
-import org.openhab.core.types.UnDefType;
-
-/**
- * The {@link Constants} General Constant Definitions
- *
- * @author Bernd Weymann - Initial contribution
- * @author Norbert Truchsess - contributor
- */
-@NonNullByDefault
-public class Constants {
- // For Vehicle Status
- public static final String OK = "Ok";
- public static final String ACTIVE = "Active";
- public static final String NOT_ACTIVE = "Not Active";
- public static final String NO_ENTRIES = "No Entries";
- public static final String OPEN = "Open";
- public static final String INVALID = "Invalid";
- public static final String CLOSED = "Closed";
- public static final String INTERMEDIATE = "Intermediate";
- public static final String UNDEF = UnDefType.UNDEF.toFullString();
- public static final String UTC_APPENDIX = "-01T12:00:00";
- public static final String NULL_DATE = "1900-01-01T00:00:00";
- public static final String NULL_TIME = "00:00";
- public static final int INT_UNDEF = -1;
- public static final Unit<Length> KILOMETRE_UNIT = MetricPrefix.KILO(SIUnits.METRE);
-
- // Services to query
- public static final String SERVICES_SUPPORTED = "servicesSupported";
- public static final String STATISTICS = "Statistics";
- public static final String LAST_DESTINATIONS = "LastDestinations";
-
- // Services in Discovery
- public static final String ACTIVATED = "ACTIVATED";
- public static final String SUPPORTED = "SUPPORTED";
- public static final String NOT_SUPPORTED = "NOT_SUPPORTED";
-
- // General Constants for String concatenation
- public static final String NULL = "null";
- public static final String SPACE = " ";
- public static final String UNDERLINE = "_";
- public static final String HYPHEN = " - ";
- public static final String PLUS = "+";
- public static final String EMPTY = "";
- public static final String COMMA = ",";
- public static final String QUESTION = "?";
- public static final String COLON = ":";
-
- public static final String ANONYMOUS = "Anonymous";
- public static final int MILES_TO_FEET_FACTOR = 5280;
- public static final String EMPTY_JSON = "{}";
-
- // Time Constants for DateTime channels
- public static final LocalDate EPOCH_DAY = LocalDate.ofEpochDay(0);
- public static final DateTimeFormatter TIME_FORMATER = DateTimeFormatter.ofPattern("HH:mm");
- public static final LocalTime NULL_LOCAL_TIME = LocalTime.parse(NULL_TIME, TIME_FORMATER);
-
- @SuppressWarnings("serial")
- public static final Map<DayOfWeek, String> DAYS = new HashMap<>() {
- {
- put(DayOfWeek.MONDAY, "Mon");
- put(DayOfWeek.TUESDAY, "Tue");
- put(DayOfWeek.WEDNESDAY, "Wed");
- put(DayOfWeek.THURSDAY, "Thu");
- put(DayOfWeek.FRIDAY, "Fri");
- put(DayOfWeek.SATURDAY, "Sat");
- put(DayOfWeek.SUNDAY, "Sun");
- }
- };
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.utils;
-
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-import javax.measure.quantity.Length;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.compat.VehicleAttributes;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.compat.VehicleAttributesContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.compat.VehicleMessages;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.CBSMessage;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.CCMMessage;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.Position;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatus;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatusContainer;
-import org.openhab.core.i18n.TimeZoneProvider;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.ImperialUnits;
-import org.openhab.core.types.State;
-import org.openhab.core.types.UnDefType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link Converter} Conversion Helpers
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class Converter {
- public static final Logger LOGGER = LoggerFactory.getLogger(Converter.class);
-
- public static final DateTimeFormatter SERVICE_DATE_INPUT_PATTERN = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- public static final DateTimeFormatter SERVICE_DATE_OUTPUT_PATTERN = DateTimeFormatter.ofPattern("MMM yyyy");
-
- public static final String LOCAL_DATE_INPUT_PATTERN_STRING = "dd.MM.yyyy HH:mm";
- public static final DateTimeFormatter LOCAL_DATE_INPUT_PATTERN = DateTimeFormatter
- .ofPattern(LOCAL_DATE_INPUT_PATTERN_STRING);
-
- public static final String DATE_INPUT_PATTERN_STRING = "yyyy-MM-dd'T'HH:mm:ss";
- public static final DateTimeFormatter DATE_INPUT_PATTERN = DateTimeFormatter.ofPattern(DATE_INPUT_PATTERN_STRING);
-
- public static final String DATE_INPUT_ZONE_PATTERN_STRING = "yyyy-MM-dd'T'HH:mm:ssZ";
- public static final DateTimeFormatter DATE_INPUT_ZONE_PATTERN = DateTimeFormatter
- .ofPattern(DATE_INPUT_ZONE_PATTERN_STRING);
-
- public static final DateTimeFormatter DATE_OUTPUT_PATTERN = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
-
- private static final Gson GSON = new Gson();
- private static final double SCALE = 10;
- public static final double MILES_TO_KM_RATIO = 1.60934;
- private static final String SPLIT_HYPHEN = "-";
- private static final String SPLIT_BRACKET = "\\(";
-
- public static Optional<TimeZoneProvider> timeZoneProvider = Optional.empty();
-
- public static double round(double value) {
- return Math.round(value * SCALE) / SCALE;
- }
-
- public static String getLocalDateTimeWithoutOffest(@Nullable String input) {
- if (input == null) {
- return Constants.NULL_DATE;
- }
- LocalDateTime ldt;
- if (input.contains(Constants.PLUS)) {
- ldt = LocalDateTime.parse(input, Converter.DATE_INPUT_ZONE_PATTERN);
- } else {
- ldt = LocalDateTime.parse(input, Converter.DATE_INPUT_PATTERN);
- }
- return ldt.format(Converter.DATE_INPUT_PATTERN);
- }
-
- public static String getLocalDateTime(@Nullable String input) {
- if (input == null) {
- return Constants.NULL_DATE;
- }
-
- LocalDateTime ldt;
- if (input.contains(Constants.PLUS)) {
- ldt = LocalDateTime.parse(input, Converter.DATE_INPUT_ZONE_PATTERN);
- } else {
- try {
- ldt = LocalDateTime.parse(input, Converter.DATE_INPUT_PATTERN);
- } catch (DateTimeParseException dtpe) {
- ldt = LocalDateTime.parse(input, Converter.LOCAL_DATE_INPUT_PATTERN);
- }
- }
- ZonedDateTime zdtUTC = ldt.atZone(ZoneId.of("UTC"));
- ZonedDateTime zdtLZ;
- zdtLZ = zdtUTC.withZoneSameInstant(ZoneId.systemDefault());
- if (timeZoneProvider.isPresent()) {
- zdtLZ = zdtUTC.withZoneSameInstant(timeZoneProvider.get().getTimeZone());
- } else {
- zdtLZ = zdtUTC.withZoneSameInstant(ZoneId.systemDefault());
- }
- return zdtLZ.format(Converter.DATE_INPUT_PATTERN);
- }
-
- public static void setTimeZoneProvider(TimeZoneProvider tzp) {
- timeZoneProvider = Optional.of(tzp);
- }
-
- public static String toTitleCase(@Nullable String input) {
- if (input == null) {
- return toTitleCase(Constants.UNDEF);
- } else {
- String lower = input.replaceAll(Constants.UNDERLINE, Constants.SPACE).toLowerCase();
- String converted = toTitleCase(lower, Constants.SPACE);
- converted = toTitleCase(converted, SPLIT_HYPHEN);
- converted = toTitleCase(converted, SPLIT_BRACKET);
- return converted;
- }
- }
-
- private static String toTitleCase(String input, String splitter) {
- String[] arr = input.split(splitter);
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < arr.length; i++) {
- if (i > 0) {
- sb.append(splitter.replaceAll("\\\\", Constants.EMPTY));
- }
- sb.append(Character.toUpperCase(arr[i].charAt(0))).append(arr[i].substring(1));
- }
- return sb.toString().trim();
- }
-
- public static String capitalizeFirst(String str) {
- return str.substring(0, 1).toUpperCase() + str.substring(1);
- }
-
- public static Gson getGson() {
- return GSON;
- }
-
- /**
- * Measure distance between 2 coordinates
- *
- * @param sourceLatitude
- * @param sourceLongitude
- * @param destinationLatitude
- * @param destinationLongitude
- * @return distance
- */
- public static double measureDistance(double sourceLatitude, double sourceLongitude, double destinationLatitude,
- double destinationLongitude) {
- double earthRadius = 6378.137; // Radius of earth in KM
- double dLat = destinationLatitude * Math.PI / 180 - sourceLatitude * Math.PI / 180;
- double dLon = destinationLongitude * Math.PI / 180 - sourceLongitude * Math.PI / 180;
- double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(sourceLatitude * Math.PI / 180)
- * Math.cos(destinationLatitude * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
- double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
- return earthRadius * c;
- }
-
- /**
- * Easy function but there's some measures behind:
- * Guessing the range of the Vehicle on Map. If you can drive x kilometers with your Vehicle it's not feasible to
- * project this x km Radius on Map. The roads to be taken are causing some overhead because they are not a straight
- * line from Location A to B.
- * I've taken some measurements to calculate the overhead factor based on Google Maps
- * Berlin - Dresden: Road Distance: 193 air-line Distance 167 = Factor 87%
- * Kassel - Frankfurt: Road Distance: 199 air-line Distance 143 = Factor 72%
- * After measuring more distances you'll find out that the outcome is between 70% and 90%. So
- *
- * This depends also on the roads of a concrete route but this is only a guess without any Route Navigation behind
- *
- * In future it's foreseen to replace this with BMW RangeMap Service which isn't running at the moment.
- *
- * @param range
- * @return mapping from air-line distance to "real road" distance
- */
- public static double guessRangeRadius(double range) {
- return range * 0.8;
- }
-
- public static State getMiles(QuantityType<Length> qtLength) {
- if (qtLength.intValue() == -1) {
- return UnDefType.UNDEF;
- }
- QuantityType<Length> qt = qtLength.toUnit(ImperialUnits.MILE);
- if (qt != null) {
- return qt;
- } else {
- LOGGER.debug("Cannot convert {} to miles", qt);
- return UnDefType.UNDEF;
- }
- }
-
- public static int getIndex(String fullString) {
- int index = -1;
- try {
- index = Integer.parseInt(fullString);
- } catch (NumberFormatException nfe) {
- }
- return index;
- }
-
- public static String transformLegacyStatus(@Nullable VehicleAttributesContainer vac) {
- if (vac != null) {
- if (vac.attributesMap != null && vac.vehicleMessages != null) {
- VehicleAttributes attributesMap = vac.attributesMap;
- VehicleMessages vehicleMessages = vac.vehicleMessages;
- // create target objects
- VehicleStatusContainer vsc = new VehicleStatusContainer();
- VehicleStatus vs = new VehicleStatus();
- vsc.vehicleStatus = vs;
-
- vs.mileage = attributesMap.mileage;
- vs.doorLockState = attributesMap.doorLockState;
-
- vs.doorDriverFront = attributesMap.doorDriverFront;
- vs.doorDriverRear = attributesMap.doorDriverRear;
- vs.doorPassengerFront = attributesMap.doorPassengerFront;
- vs.doorPassengerRear = attributesMap.doorPassengerRear;
- vs.hood = attributesMap.hoodState;
- vs.trunk = attributesMap.trunkState;
-
- vs.windowDriverFront = attributesMap.winDriverFront;
- vs.windowDriverRear = attributesMap.winDriverRear;
- vs.windowPassengerFront = attributesMap.winPassengerFront;
- vs.windowPassengerRear = attributesMap.winPassengerRear;
- vs.sunroof = attributesMap.sunroofState;
-
- vs.remainingFuel = attributesMap.remainingFuel;
- vs.remainingRangeElectric = attributesMap.beRemainingRangeElectricKm;
- vs.remainingRangeElectricMls = attributesMap.beRemainingRangeElectricMile;
- vs.remainingRangeFuel = attributesMap.beRemainingRangeFuelKm;
- vs.remainingRangeFuelMls = attributesMap.beRemainingRangeFuelMile;
- vs.remainingFuel = attributesMap.remainingFuel;
- vs.chargingLevelHv = attributesMap.chargingLevelHv;
- vs.maxRangeElectric = attributesMap.beMaxRangeElectric;
- vs.maxRangeElectricMls = attributesMap.beMaxRangeElectricMile;
- vs.chargingStatus = attributesMap.chargingHVStatus;
- vs.connectionStatus = attributesMap.connectorStatus;
- vs.lastChargingEndReason = attributesMap.lastChargingEndReason;
-
- vs.updateTime = attributesMap.updateTimeConverted;
- vs.updateReason = attributesMap.lastUpdateReason;
-
- Position p = new Position();
- p.lat = attributesMap.gpsLat;
- p.lon = attributesMap.gpsLon;
- p.heading = attributesMap.heading;
- vs.position = p;
-
- final List<CCMMessage> ccml = new ArrayList<CCMMessage>();
- if (vehicleMessages != null) {
- if (vehicleMessages.ccmMessages != null) {
- vehicleMessages.ccmMessages.forEach(entry -> {
- CCMMessage ccmM = new CCMMessage();
- ccmM.ccmDescriptionShort = entry.text;
- ccmM.ccmDescriptionLong = Constants.HYPHEN;
- ccmM.ccmMileage = entry.unitOfLengthRemaining;
- ccml.add(ccmM);
- });
- }
- }
- vs.checkControlMessages = ccml;
-
- final List<CBSMessage> cbsl = new ArrayList<CBSMessage>();
- if (vehicleMessages != null) {
- if (vehicleMessages.cbsMessages != null) {
- vehicleMessages.cbsMessages.forEach(entry -> {
- CBSMessage cbsm = new CBSMessage();
- cbsm.cbsType = entry.text;
- cbsm.cbsDescription = entry.description;
- cbsm.cbsDueDate = entry.date;
- cbsm.cbsRemainingMileage = entry.unitOfLengthRemaining;
- cbsl.add(cbsm);
- });
- }
- }
- vs.cbsData = cbsl;
- return Converter.getGson().toJson(vsc);
- }
- }
- return Constants.EMPTY_JSON;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.utils;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link HTTPConstants} class contains fields mapping thing configuration parameters.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class HTTPConstants {
- public static final int HTTP_TIMEOUT_SEC = 10;
-
- public static final String AUTH_HTTP_CLIENT_NAME = "AuthHttpClient";
- public static final String CONTENT_TYPE_URL_ENCODED = "application/x-www-form-urlencoded";
- public static final String CONTENT_TYPE_JSON_ENCODED = "application/json";
- public static final String KEEP_ALIVE = "Keep-Alive";
- public static final String CLIENT_ID = "client_id";
- public static final String RESPONSE_TYPE = "response_type";
- public static final String TOKEN = "token";
- public static final String CODE = "code";
- public static final String REDIRECT_URI = "redirect_uri";
- public static final String AUTHORIZATION = "authorization";
- public static final String GRANT_TYPE = "grant_type";
- public static final String SCOPE = "scope";
- public static final String CREDENTIALS = "Credentials";
- public static final String USERNAME = "username";
- public static final String PASSWORD = "password";
- public static final String CONTENT_LENGTH = "Content-Length";
-
- public static final String ACCESS_TOKEN = "access_token";
- public static final String TOKEN_TYPE = "token_type";
- public static final String EXPIRES_IN = "expires_in";
- public static final String CHUNKED = "chunked";
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.utils;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link ImageProperties} Properties of current Vehicle Image
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class ImageProperties {
- public static final int RETRY_COUNTER = 5;
- public int failCounter = 0;
- public String viewport = Constants.EMPTY;
- public int size = -1;
-
- public ImageProperties(String viewport, int size) {
- this.viewport = viewport;
- this.size = size;
- }
-
- public ImageProperties() {
- }
-
- public void failed() {
- failCounter++;
- }
-
- public boolean failLimitReached() {
- return failCounter > RETRY_COUNTER;
- }
-
- @Override
- public String toString() {
- return viewport + size;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.utils;
-
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.RemoteServiceHandler.RemoteService;
-import org.openhab.core.types.StateOption;
-
-/**
- * Helper class for Remote Service Commands
- *
- * @author Norbert Truchsess - Initial contribution
- */
-@NonNullByDefault
-public class RemoteServiceUtils {
-
- private static final Map<String, RemoteService> COMMAND_SERVICES = Stream.of(RemoteService.values())
- .collect(Collectors.toUnmodifiableMap(RemoteService::getCommand, service -> service));
-
- private static final Set<RemoteService> ELECTRIC_SERVICES = EnumSet.of(RemoteService.CHARGE_NOW,
- RemoteService.CHARGING_CONTROL);
-
- public static Optional<RemoteService> getRemoteService(final String command) {
- return Optional.ofNullable(COMMAND_SERVICES.get(command));
- }
-
- public static List<StateOption> getOptions(final boolean isElectric) {
- return Stream.of(RemoteService.values())
- .filter(service -> isElectric ? true : !ELECTRIC_SERVICES.contains(service))
- .map(service -> new StateOption(service.getCommand(), service.getLabel()))
- .collect(Collectors.toUnmodifiableList());
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.utils;
-
-import static org.openhab.binding.bmwconnecteddrive.internal.utils.Constants.*;
-
-import java.lang.reflect.Field;
-import java.time.LocalDateTime;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.CBSMessage;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatus;
-
-/**
- * The {@link VehicleStatusUtils} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class VehicleStatusUtils {
-
- public static String getNextServiceDate(VehicleStatus vStatus) {
- if (vStatus.cbsData == null) {
- return Constants.NULL_DATE;
- }
- if (vStatus.cbsData.isEmpty()) {
- return Constants.NULL_DATE;
- } else {
- LocalDateTime farFuture = LocalDateTime.now().plusYears(100);
- LocalDateTime serviceDate = farFuture;
- for (int i = 0; i < vStatus.cbsData.size(); i++) {
- CBSMessage entry = vStatus.cbsData.get(i);
- if (entry.cbsDueDate != null) {
- LocalDateTime d = LocalDateTime.parse(entry.cbsDueDate + Constants.UTC_APPENDIX);
- if (d.isBefore(serviceDate)) {
- serviceDate = d;
- }
- }
- }
- if (serviceDate.equals(farFuture)) {
- return Constants.NULL_DATE;
- } else {
- return serviceDate.format(Converter.DATE_INPUT_PATTERN);
- }
- }
- }
-
- public static int getNextServiceMileage(VehicleStatus vStatus) {
- if (vStatus.cbsData == null) {
- return -1;
- }
- if (vStatus.cbsData.isEmpty()) {
- return -1;
- } else {
- int serviceMileage = Integer.MAX_VALUE;
- for (int i = 0; i < vStatus.cbsData.size(); i++) {
- CBSMessage entry = vStatus.cbsData.get(i);
- if (entry.cbsRemainingMileage != -1) {
- if (entry.cbsRemainingMileage < serviceMileage) {
- serviceMileage = entry.cbsRemainingMileage;
- }
- }
- }
- if (serviceMileage != Integer.MAX_VALUE) {
- return serviceMileage;
- } else {
- return -1;
- }
- }
- }
-
- public static String checkControlActive(VehicleStatus vStatus) {
- if (vStatus.checkControlMessages == null) {
- return UNDEF;
- }
- if (vStatus.checkControlMessages.isEmpty()) {
- return NOT_ACTIVE;
- } else {
- return ACTIVE;
- }
- }
-
- public static String getUpdateTime(VehicleStatus vStatus) {
- if (vStatus.internalDataTimeUTC != null) {
- return vStatus.internalDataTimeUTC;
- } else if (vStatus.updateTime != null) {
- return vStatus.updateTime;
- } else {
- return Constants.NULL_DATE;
- }
- }
-
- /**
- * Check for certain Windows or Doors DTO object the "Closed" Status
- * INVALID values will be ignored
- *
- * @param dto
- * @return Closed if all "Closed", "Open" otherwise
- */
- public static String checkClosed(Object dto) {
- String overallState = Constants.UNDEF;
- for (Field field : dto.getClass().getDeclaredFields()) {
- try {
- Object d = field.get(dto);
- if (d != null) {
- String state = d.toString();
- // skip invalid entries - they don't apply to this Vehicle
- if (!state.equalsIgnoreCase(INVALID)) {
- if (state.equalsIgnoreCase(OPEN)) {
- overallState = OPEN;
- // stop searching for more open items - overall Doors / Windows are OPEN
- break;
- } else if (state.equalsIgnoreCase(INTERMEDIATE)) {
- if (!overallState.equalsIgnoreCase(OPEN)) {
- overallState = INTERMEDIATE;
- // continue searching - maybe another Door / Window is OPEN
- }
- } else if (state.equalsIgnoreCase(CLOSED)) {
- // at least one valid object needs to be found in order to reply "CLOSED"
- if (overallState.equalsIgnoreCase(UNDEF)) {
- overallState = CLOSED;
- }
- }
- }
- }
- } catch (IllegalArgumentException | IllegalAccessException e) {
- }
- }
- return Converter.toTitleCase(overallState);
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<binding:binding id="bmwconnecteddrive" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
- xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
-
- <name>BMW ConnectedDrive</name>
- <description>Provides access to your Vehicle Data via BMW Connected Drive Portal</description>
-
-</binding:binding>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<config-description:config-descriptions
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
- 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:bmwconnecteddrive:bridge">
- <parameter name="userName" type="text" required="true">
- <label>Username</label>
- <description>BMW Connected Drive Username</description>
- </parameter>
- <parameter name="password" type="text" required="true">
- <label>Password</label>
- <description>BMW Connected Drive Password</description>
- <context>password</context>
- </parameter>
- <parameter name="region" type="text" required="true">
- <label>Region</label>
- <description>Select Region in order to connect to the appropriate BMW Server</description>
- <options>
- <option value="NORTH_AMERICA">North America</option>
- <option value="CHINA">China</option>
- <option value="ROW">Rest of the World</option>
- </options>
- <default>ROW</default>
- </parameter>
- <parameter name="preferMyBmw" type="boolean" required="false">
- <label>Prefer MyBMW API</label>
- <description>Prefer *MyBMW* API instead of *BMW Connected Drive*</description>
- <advanced>true</advanced>
- <default>false</default>
- </parameter>
- </config-description>
-</config-description:config-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<config-description:config-descriptions
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
- 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:bmwconnecteddrive:vehicle">
- <parameter name="vin" type="text" required="true">
- <label>Vehicle Identification Number (VIN)</label>
- <description>Unique VIN given by BMW</description>
- </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="units" type="text">
- <label>Unit Selection</label>
- <description>Units are selected via auto-detection but you can overrule</description>
- <options>
- <option value="AUTODETECT">Auto Detect</option>
- <option value="METRIC">Metric</option>
- <option value="IMPERIAL">Imperial</option>
- </options>
- <default>AUTODETECT</default>
- </parameter>
- <parameter name="imageSize" type="integer">
- <label>Image Picture Size</label>
- <description>Vehicle Image size for width and length</description>
- <default>1024</default>
- </parameter>
- <parameter name="imageViewport" type="text">
- <label>Image Viewport</label>
- <description>Viewport for Vehicle Image</description>
- <options>
- <option value="FRONT">Front View</option>
- <option value="REAR">Rear View</option>
- <option value="SIDE">Side View</option>
- <option value="DASHBOARD">Dashboard View</option>
- <option value="DRIVERDOOR">Driver Door View</option>
- </options>
- <default>FRONT</default>
- </parameter>
- </config-description>
-</config-description:config-descriptions>
+++ /dev/null
-# binding
-
-binding.bmwconnecteddrive.name = BMW ConnectedDrive
-binding.bmwconnecteddrive.description = Provides access to your Vehicle Data via BMW Connected Drive Portal
-
-# thing types
-
-thing-type.bmwconnecteddrive.account.label = BMW ConnectedDrive Account
-thing-type.bmwconnecteddrive.account.description = Access to BMW ConnectedDrive Portal for a specific user
-thing-type.bmwconnecteddrive.bev.label = Electric Vehicle
-thing-type.bmwconnecteddrive.bev.description = Battery Electric Vehicle (BEV)
-thing-type.bmwconnecteddrive.bev_rex.label = Electric Vehicle with REX
-thing-type.bmwconnecteddrive.bev_rex.description = Battery Electric Vehicle with Range Extender (BEV_REX)
-thing-type.bmwconnecteddrive.conv.label = Conventional Vehicle
-thing-type.bmwconnecteddrive.conv.description = Conventional Fuel Vehicle (CONV)
-thing-type.bmwconnecteddrive.phev.label = Plug-In-Hybrid Electric Vehicle
-thing-type.bmwconnecteddrive.phev.description = Conventional Fuel Vehicle with supporting Electric Engine (PHEV)
-
-# thing types config
-
-thing-type.config.bmwconnecteddrive.bridge.password.label = Password
-thing-type.config.bmwconnecteddrive.bridge.password.description = BMW Connected Drive Password
-thing-type.config.bmwconnecteddrive.bridge.preferMyBmw.label = Prefer MyBMW API
-thing-type.config.bmwconnecteddrive.bridge.preferMyBmw.description = Prefer *MyBMW* API instead of *BMW Connected Drive*
-thing-type.config.bmwconnecteddrive.bridge.region.label = Region
-thing-type.config.bmwconnecteddrive.bridge.region.description = Select Region in order to connect to the appropriate BMW Server
-thing-type.config.bmwconnecteddrive.bridge.region.option.NORTH_AMERICA = North America
-thing-type.config.bmwconnecteddrive.bridge.region.option.CHINA = China
-thing-type.config.bmwconnecteddrive.bridge.region.option.ROW = Rest of the World
-thing-type.config.bmwconnecteddrive.bridge.userName.label = Username
-thing-type.config.bmwconnecteddrive.bridge.userName.description = BMW Connected Drive Username
-thing-type.config.bmwconnecteddrive.vehicle.imageSize.label = Image Picture Size
-thing-type.config.bmwconnecteddrive.vehicle.imageSize.description = Vehicle Image size for width and length
-thing-type.config.bmwconnecteddrive.vehicle.imageViewport.label = Image Viewport
-thing-type.config.bmwconnecteddrive.vehicle.imageViewport.description = Viewport for Vehicle Image
-thing-type.config.bmwconnecteddrive.vehicle.imageViewport.option.FRONT = Front View
-thing-type.config.bmwconnecteddrive.vehicle.imageViewport.option.REAR = Rear View
-thing-type.config.bmwconnecteddrive.vehicle.imageViewport.option.SIDE = Side View
-thing-type.config.bmwconnecteddrive.vehicle.imageViewport.option.DASHBOARD = Dashboard View
-thing-type.config.bmwconnecteddrive.vehicle.imageViewport.option.DRIVERDOOR = Driver Door View
-thing-type.config.bmwconnecteddrive.vehicle.refreshInterval.label = Refresh Interval
-thing-type.config.bmwconnecteddrive.vehicle.refreshInterval.description = Data Refresh Rate for your Vehicle data
-thing-type.config.bmwconnecteddrive.vehicle.units.label = Unit Selection
-thing-type.config.bmwconnecteddrive.vehicle.units.description = Units are selected via auto-detection but you can overrule
-thing-type.config.bmwconnecteddrive.vehicle.units.option.AUTODETECT = Auto Detect
-thing-type.config.bmwconnecteddrive.vehicle.units.option.METRIC = Metric
-thing-type.config.bmwconnecteddrive.vehicle.units.option.IMPERIAL = Imperial
-thing-type.config.bmwconnecteddrive.vehicle.vin.label = Vehicle Identification Number (VIN)
-thing-type.config.bmwconnecteddrive.vehicle.vin.description = Unique VIN given by BMW
-
-# channel group types
-
-channel-group-type.bmwconnecteddrive.charge-values.label = Electric Charging
-channel-group-type.bmwconnecteddrive.charge-values.description = Charge Profiles of Vehicle
-channel-group-type.bmwconnecteddrive.check-control-values.label = Check Control Messages
-channel-group-type.bmwconnecteddrive.check-control-values.description = Show the current active CheckControl Messages
-channel-group-type.bmwconnecteddrive.conv-range-values.label = Range Data
-channel-group-type.bmwconnecteddrive.conv-range-values.description = Provides Mileage, remaining range and fuel level values
-channel-group-type.bmwconnecteddrive.destination-values.label = Destination List
-channel-group-type.bmwconnecteddrive.destination-values.description = Shows Your Destinations in a List
-channel-group-type.bmwconnecteddrive.door-values.label = Detailed Door Status
-channel-group-type.bmwconnecteddrive.door-values.description = Detailed Status of all Doors and Windows
-channel-group-type.bmwconnecteddrive.ev-last-trip-values.label = Last Trip Statistics
-channel-group-type.bmwconnecteddrive.ev-last-trip-values.description = EV Consumption Values and Distances for the Last Trip
-channel-group-type.bmwconnecteddrive.ev-lifetime-values.label = Lifetime Statistics
-channel-group-type.bmwconnecteddrive.ev-lifetime-values.description = Consumption Values and Distances over Lifetime
-channel-group-type.bmwconnecteddrive.ev-range-values.label = Electric Range Data
-channel-group-type.bmwconnecteddrive.ev-range-values.description = Provides Mileage, remaining range and charge level values
-channel-group-type.bmwconnecteddrive.ev-vehicle-status.label = Vehicle Status
-channel-group-type.bmwconnecteddrive.ev-vehicle-status.description = Provides Status of Doors, Windows, Lock State, Service and Check Control Messages
-channel-group-type.bmwconnecteddrive.hybrid-last-trip-values.label = Last Trip Statistics
-channel-group-type.bmwconnecteddrive.hybrid-last-trip-values.description = Hybrid Consumption Values and Distances for the Last Trip
-channel-group-type.bmwconnecteddrive.hybrid-lifetime-values.label = Lifetime Statistics
-channel-group-type.bmwconnecteddrive.hybrid-lifetime-values.description = Consumption Values and Distances over Lifetime
-channel-group-type.bmwconnecteddrive.hybrid-range-values.label = Hybrid Range Data
-channel-group-type.bmwconnecteddrive.hybrid-range-values.description = Provides Mileage, remaining range and fuel and charge level values
-channel-group-type.bmwconnecteddrive.image-values.label = Vehicle Image
-channel-group-type.bmwconnecteddrive.image-values.description = Provides an Image of your Vehicle
-channel-group-type.bmwconnecteddrive.location-values.label = Vehicle Location
-channel-group-type.bmwconnecteddrive.location-values.description = Coordinates and Heading of the Vehcile
-channel-group-type.bmwconnecteddrive.remote-services.label = Remote Services
-channel-group-type.bmwconnecteddrive.remote-services.description = Services can be executed via BMW Server like Door lock/unlock, Air Conditioning and more
-channel-group-type.bmwconnecteddrive.service-values.label = Vehicle Services
-channel-group-type.bmwconnecteddrive.service-values.description = All future Service schedules
-channel-group-type.bmwconnecteddrive.vehicle-status.label = Vehicle Status
-channel-group-type.bmwconnecteddrive.vehicle-status.description = Provides Status of Doors, Windows, Lock State, Service and Check Control Messages
-
-# channel types
-
-channel-type.bmwconnecteddrive.average-combined-consumption-channel.label = Avg. Combined Consumption
-channel-type.bmwconnecteddrive.average-combined-consumption-channel.description = Average combined consumption in liter per 100 km/mi
-channel-type.bmwconnecteddrive.average-combined-consumption-channel.label = Avg. Combined Consumption
-channel-type.bmwconnecteddrive.average-combined-consumption-channel.description = Average combined consumption in liter per 100 km/mi
-channel-type.bmwconnecteddrive.average-consumption-channel.label = Avg. Power Consumption
-channel-type.bmwconnecteddrive.average-consumption-channel.description = Average Combined Consumption electric power consumption per 100 km/mi
-channel-type.bmwconnecteddrive.average-consumption-channel.label = Avg. Power Consumption
-channel-type.bmwconnecteddrive.average-consumption-channel.description = Average electric power consumption per 100 km/mi
-channel-type.bmwconnecteddrive.average-recuperation-channel.label = Avg. Combined Consumption Recuperation
-channel-type.bmwconnecteddrive.average-recuperation-channel.description = Average electric recuperation per 100 km/mi
-channel-type.bmwconnecteddrive.average-recuperation-channel.label = Avg. Recuperation
-channel-type.bmwconnecteddrive.average-recuperation-channel.description = Average electric recuperation per 100 km/mi
-channel-type.bmwconnecteddrive.charging-remaining-channel.label = Remaining Charging Time
-channel-type.bmwconnecteddrive.charging-status-channel.label = Charging Status
-channel-type.bmwconnecteddrive.check-control-channel.label = Check Control
-channel-type.bmwconnecteddrive.checkcontrol-details-channel.label = CheckControl Details
-channel-type.bmwconnecteddrive.checkcontrol-mileage-channel.label = Mileage Occurrence
-channel-type.bmwconnecteddrive.checkcontrol-name-channel.label = CheckControl Description
-channel-type.bmwconnecteddrive.destination-gps-channel.label = GPS Coordinates
-channel-type.bmwconnecteddrive.destination-name-channel.label = Name
-channel-type.bmwconnecteddrive.distance-channel.label = Last Trip Distance
-channel-type.bmwconnecteddrive.distance-since-charging-channel.label = Distance since Charge
-channel-type.bmwconnecteddrive.distance-since-charging-channel.description = Total distance driven since last charging
-channel-type.bmwconnecteddrive.doors-channel.label = Overall Door Status
-channel-type.bmwconnecteddrive.driver-front-channel.label = Driver Door
-channel-type.bmwconnecteddrive.driver-rear-channel.label = Driver Door Rear
-channel-type.bmwconnecteddrive.gps-channel.label = GPS Coordinates
-channel-type.bmwconnecteddrive.heading-channel.label = Heading Angle
-channel-type.bmwconnecteddrive.hood-channel.label = Hood
-channel-type.bmwconnecteddrive.image-size-channel.label = Image Size
-channel-type.bmwconnecteddrive.image-view-channel.label = Image Viewport
-channel-type.bmwconnecteddrive.image-view-channel.command.option.FRONT = Front View
-channel-type.bmwconnecteddrive.image-view-channel.command.option.REAR = Rear View
-channel-type.bmwconnecteddrive.image-view-channel.command.option.SIDE = Side View
-channel-type.bmwconnecteddrive.image-view-channel.command.option.DASHBOARD = Dashboard View
-channel-type.bmwconnecteddrive.image-view-channel.command.option.DRIVERDOOR = Driver Door View
-channel-type.bmwconnecteddrive.last-update-channel.label = Last Status Timestamp
-channel-type.bmwconnecteddrive.last-update-channel.state.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
-channel-type.bmwconnecteddrive.last-update-reason-channel.label = Last Status Timestamp Reason
-channel-type.bmwconnecteddrive.lock-channel.label = Doors Locked
-channel-type.bmwconnecteddrive.mileage-channel.label = Total Distance Driven
-channel-type.bmwconnecteddrive.next-service-date-channel.label = Next Service Date
-channel-type.bmwconnecteddrive.next-service-date-channel.state.pattern = %1$tb %1$tY
-channel-type.bmwconnecteddrive.next-service-mileage-channel.label = Mileage Till Next Service
-channel-type.bmwconnecteddrive.override-departure-channel.label = OT Departure Time
-channel-type.bmwconnecteddrive.override-departure-channel.description = Departure time for override timer
-channel-type.bmwconnecteddrive.override-departure-channel.state.pattern = %1$tH:%1$tM
-channel-type.bmwconnecteddrive.override-enabled-channel.label = OT Enabled
-channel-type.bmwconnecteddrive.override-enabled-channel.description = Override timer enabled
-channel-type.bmwconnecteddrive.passenger-front-channel.label = Passenger Door
-channel-type.bmwconnecteddrive.passenger-rear-channel.label = Passenger Door Rear
-channel-type.bmwconnecteddrive.plug-connection-channel.label = Plug Connection Status
-channel-type.bmwconnecteddrive.png-channel.label = Rendered Vehicle Image
-channel-type.bmwconnecteddrive.profile-climate-channel.label = A/C at Departure Time
-channel-type.bmwconnecteddrive.profile-mode-channel.label = Charge Mode
-channel-type.bmwconnecteddrive.profile-mode-channel.description = Mode for selecting immediate or delyed charging
-channel-type.bmwconnecteddrive.profile-mode-channel.command.option.IMMEDIATE_CHARGING = Immediate Charging
-channel-type.bmwconnecteddrive.profile-mode-channel.command.option.DELAYED_CHARGING = Prefer Charging in Charging Window
-channel-type.bmwconnecteddrive.profile-prefs-channel.label = Charge Preferences
-channel-type.bmwconnecteddrive.profile-prefs-channel.description = Preferences for delayed charging
-channel-type.bmwconnecteddrive.profile-prefs-channel.command.option.NO_PRESELECTION = No Preference
-channel-type.bmwconnecteddrive.profile-prefs-channel.command.option.CHARGING_WINDOW = Charging Window
-channel-type.bmwconnecteddrive.range-electric-channel.label = Electric Range
-channel-type.bmwconnecteddrive.range-electric-max-channel.label = Electric Range if Fully Charged
-channel-type.bmwconnecteddrive.range-fuel-channel.label = Fuel Range
-channel-type.bmwconnecteddrive.range-hybrid-channel.label = Hybrid Range
-channel-type.bmwconnecteddrive.range-hybrid-max-channel.label = Hybrid Range if Fully Charged
-channel-type.bmwconnecteddrive.range-radius-electric-channel.label = Electric Range Radius if Fully Charged
-channel-type.bmwconnecteddrive.range-radius-electric-max-channel.label = Electric Range Radius
-channel-type.bmwconnecteddrive.range-radius-fuel-channel.label = Fuel Range Radius
-channel-type.bmwconnecteddrive.range-radius-hybrid-channel.label = Hybrid Range Radius
-channel-type.bmwconnecteddrive.range-radius-hybrid-max-channel.label = Hybrid Range Radius if Fully Charged
-channel-type.bmwconnecteddrive.remaining-fuel-channel.label = Remaining Fuel
-channel-type.bmwconnecteddrive.remote-command-channel.label = Remote Command
-channel-type.bmwconnecteddrive.remote-state-channel.label = Service Execution State
-channel-type.bmwconnecteddrive.service-date-channel.label = Service Date
-channel-type.bmwconnecteddrive.service-date-channel.state.pattern = %1$tb %1$tY
-channel-type.bmwconnecteddrive.service-details-channel.label = Service Details
-channel-type.bmwconnecteddrive.service-mileage-channel.label = Mileage till Service
-channel-type.bmwconnecteddrive.service-name-channel.label = Service Name
-channel-type.bmwconnecteddrive.single-longest-distance-channel.label = Longest 1-Charge Distance
-channel-type.bmwconnecteddrive.soc-channel.label = Battery Charge Level
-channel-type.bmwconnecteddrive.soc-max-channel.label = Max Battery Capacity
-channel-type.bmwconnecteddrive.sunroof-channel.label = Sunroof
-channel-type.bmwconnecteddrive.timer1-day-fri-channel.label = T1 Friday
-channel-type.bmwconnecteddrive.timer1-day-fri-channel.description = Friday scheduled for timer 1
-channel-type.bmwconnecteddrive.timer1-day-mon-channel.label = T1 Monday
-channel-type.bmwconnecteddrive.timer1-day-mon-channel.description = Monday scheduled for timer 1
-channel-type.bmwconnecteddrive.timer1-day-sat-channel.label = T1 Saturday
-channel-type.bmwconnecteddrive.timer1-day-sat-channel.description = Saturday scheduled for timer 1
-channel-type.bmwconnecteddrive.timer1-day-sun-channel.label = T1 Sunday
-channel-type.bmwconnecteddrive.timer1-day-sun-channel.description = Sunday scheduled for timer 1
-channel-type.bmwconnecteddrive.timer1-day-thu-channel.label = T1 Thursday
-channel-type.bmwconnecteddrive.timer1-day-thu-channel.description = Thursday scheduled for timer 1
-channel-type.bmwconnecteddrive.timer1-day-tue-channel.label = T1 Tuesday
-channel-type.bmwconnecteddrive.timer1-day-tue-channel.description = Tuesday scheduled for timer 1
-channel-type.bmwconnecteddrive.timer1-day-wed-channel.label = T1 Wednesday
-channel-type.bmwconnecteddrive.timer1-day-wed-channel.description = Wednesday scheduled for timer 1
-channel-type.bmwconnecteddrive.timer1-days-channel.label = T1 Days
-channel-type.bmwconnecteddrive.timer1-days-channel.description = Days scheduled for timer 1
-channel-type.bmwconnecteddrive.timer1-departure-channel.label = T1 Departure Time
-channel-type.bmwconnecteddrive.timer1-departure-channel.description = Departure time for regular schedule timer 1
-channel-type.bmwconnecteddrive.timer1-departure-channel.state.pattern = %1$tH:%1$tM
-channel-type.bmwconnecteddrive.timer1-enabled-channel.label = T1 Enabled
-channel-type.bmwconnecteddrive.timer1-enabled-channel.description = Timer 1 enabled
-channel-type.bmwconnecteddrive.timer2-day-fri-channel.label = T2 Friday
-channel-type.bmwconnecteddrive.timer2-day-fri-channel.description = Friday scheduled for timer 2
-channel-type.bmwconnecteddrive.timer2-day-mon-channel.label = T2 Monday
-channel-type.bmwconnecteddrive.timer2-day-mon-channel.description = Monday scheduled for timer 2
-channel-type.bmwconnecteddrive.timer2-day-sat-channel.label = T2 Saturday
-channel-type.bmwconnecteddrive.timer2-day-sat-channel.description = Saturday scheduled for timer 2
-channel-type.bmwconnecteddrive.timer2-day-sun-channel.label = T2 Sunday
-channel-type.bmwconnecteddrive.timer2-day-sun-channel.description = Sunday scheduled for timer 2
-channel-type.bmwconnecteddrive.timer2-day-thu-channel.label = T2 Thursday
-channel-type.bmwconnecteddrive.timer2-day-thu-channel.description = Thursday scheduled for timer 2
-channel-type.bmwconnecteddrive.timer2-day-tue-channel.label = T2 Tuesday
-channel-type.bmwconnecteddrive.timer2-day-tue-channel.description = Tuesday scheduled for timer 2
-channel-type.bmwconnecteddrive.timer2-day-wed-channel.label = T2 Wednesday
-channel-type.bmwconnecteddrive.timer2-day-wed-channel.description = Wednesday scheduled for timer 2
-channel-type.bmwconnecteddrive.timer2-days-channel.label = T2 Days
-channel-type.bmwconnecteddrive.timer2-days-channel.description = Days scheduled for timer 2
-channel-type.bmwconnecteddrive.timer2-departure-channel.label = T2 Departure Time
-channel-type.bmwconnecteddrive.timer2-departure-channel.description = Departure time for regular schedule timer 2
-channel-type.bmwconnecteddrive.timer2-departure-channel.state.pattern = %1$tH:%1$tM
-channel-type.bmwconnecteddrive.timer2-enabled-channel.label = T2 Enabled
-channel-type.bmwconnecteddrive.timer2-enabled-channel.description = Timer 2 enabled
-channel-type.bmwconnecteddrive.timer3-day-fri-channel.label = T3 Friday
-channel-type.bmwconnecteddrive.timer3-day-fri-channel.description = Friday scheduled for timer 3
-channel-type.bmwconnecteddrive.timer3-day-mon-channel.label = T3 Monday
-channel-type.bmwconnecteddrive.timer3-day-mon-channel.description = Monday scheduled for timer 3
-channel-type.bmwconnecteddrive.timer3-day-sat-channel.label = T3 Saturday
-channel-type.bmwconnecteddrive.timer3-day-sat-channel.description = Saturday scheduled for timer 3
-channel-type.bmwconnecteddrive.timer3-day-sun-channel.label = T3 Sunday
-channel-type.bmwconnecteddrive.timer3-day-sun-channel.description = Sunday scheduled for timer 3
-channel-type.bmwconnecteddrive.timer3-day-thu-channel.label = T3 Thursday
-channel-type.bmwconnecteddrive.timer3-day-thu-channel.description = Thursday scheduled for timer 3
-channel-type.bmwconnecteddrive.timer3-day-tue-channel.label = T3 Tuesday
-channel-type.bmwconnecteddrive.timer3-day-tue-channel.description = Tuesday scheduled for timer 3
-channel-type.bmwconnecteddrive.timer3-day-wed-channel.label = T3 Wednesday
-channel-type.bmwconnecteddrive.timer3-day-wed-channel.description = Wednesday scheduled for timer 3
-channel-type.bmwconnecteddrive.timer3-days-channel.label = T3 Days
-channel-type.bmwconnecteddrive.timer3-days-channel.description = Days scheduled for timer 3
-channel-type.bmwconnecteddrive.timer3-departure-channel.label = T3 Departure Time
-channel-type.bmwconnecteddrive.timer3-departure-channel.description = Departure time for regular schedule timer 3
-channel-type.bmwconnecteddrive.timer3-departure-channel.state.pattern = %1$tH:%1$tM
-channel-type.bmwconnecteddrive.timer3-enabled-channel.label = T3 Enabled
-channel-type.bmwconnecteddrive.timer3-enabled-channel.description = Timer 3 enabled
-channel-type.bmwconnecteddrive.total-driven-distance-channel.label = Total Electric Distance
-channel-type.bmwconnecteddrive.trip-date-time-channel.label = Date and Time
-channel-type.bmwconnecteddrive.trip-date-time-channel.state.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
-channel-type.bmwconnecteddrive.trip-duration-channel.label = Last Trip Duration
-channel-type.bmwconnecteddrive.trunk-channel.label = Trunk
-channel-type.bmwconnecteddrive.window-driver-front-channel.label = Driver Window
-channel-type.bmwconnecteddrive.window-driver-rear-channel.label = Driver Rear Window
-channel-type.bmwconnecteddrive.window-end-channel.label = Window End Time
-channel-type.bmwconnecteddrive.window-end-channel.description = End time of charging window
-channel-type.bmwconnecteddrive.window-end-channel.state.pattern = %1$tH:%1$tM
-channel-type.bmwconnecteddrive.window-passenger-front-channel.label = Passenger Window
-channel-type.bmwconnecteddrive.window-passenger-rear-channel.label = Passenger Rear Window
-channel-type.bmwconnecteddrive.window-rear-channel.label = Rear Window
-channel-type.bmwconnecteddrive.window-start-channel.label = Window Start Time
-channel-type.bmwconnecteddrive.window-start-channel.description = Start time of charging window
-channel-type.bmwconnecteddrive.window-start-channel.state.pattern = %1$tH:%1$tM
-channel-type.bmwconnecteddrive.windows-channel.label = Overall Window Status
+++ /dev/null
-# Binding
-binding.bmwconnecteddrive.name = BMW ConnectedDrive
-binding.bmwconnecteddrive.description = Zeigt die Fahrzeugdaten über das BMW ConnectedDrive Portal
-
-# bridge types
-thing-type.bmwconnecteddrive.account.label = BMW ConnectedDrive Benutzerkonto
-thing-type.bmwconnecteddrive.account.description = Zugriff auf das BMW ConnectedDrive Portal für einen Benutzer
-thing-type.config.bmwconnecteddrive.account.userName.label = Benutzername
-thing-type.config.bmwconnecteddrive.account.userName.description = Benutzername für das ConnectedDrive Portal
-thing-type.config.bmwconnecteddrive.account.password.label = Passwort
-thing-type.config.bmwconnecteddrive.account.password.description = Passwort für das ConnectedDrive Portal
-thing-type.config.bmwconnecteddrive.account.region.label = Region
-thing-type.config.bmwconnecteddrive.account.region.description = Auswahl Ihrer Region zur Verbindung mit dem korrekten BMW Server
-thing-type.config.bmwconnecteddrive.account.region.option.NORTH_AMERICA = Nordamerika
-thing-type.config.bmwconnecteddrive.account.region.option.CHINA = China
-thing-type.config.bmwconnecteddrive.account.region.option.ROW = Rest der Welt
-thing-type.config.bmwconnecteddrive.account.preferMyBmw.label = Benutze MyBMW API
-thing-type.config.bmwconnecteddrive.account.preferMyBmw.description = Benutzung des MyBMW API anstelle der BMW ConnectedDrive API
-
-# thing types
-thing-type.bmwconnecteddrive.bev_rex.label = Elektrofahrzeug mit REX
-thing-type.bmwconnecteddrive.bev_rex.description = Elektrofahrzeug mit Range Extender (bev_rex)
-thing-type.config.bmwconnecteddrive.bev_rex.vin.label = Fahrzeug Identifikationsnummer (VIN)
-thing-type.config.bmwconnecteddrive.bev_rex.vin.description = VIN des Fahrzeugs
-thing-type.config.bmwconnecteddrive.bev_rex.refreshInterval.label = Datenaktualisierung in Minuten
-thing-type.config.bmwconnecteddrive.bev_rex.refreshInterval.description = Rate der Datenaktualisierung Ihres Fahrzeugs
-thing-type.config.bmwconnecteddrive.bev_rex.units.label = Einheiten
-thing-type.config.bmwconnecteddrive.bev_rex.units.description = Automatische oder direkte Auswahl der Einheiten
-thing-type.config.bmwconnecteddrive.bev_rex.units.option.AUTODETECT = Automatische Auswahl
-thing-type.config.bmwconnecteddrive.bev_rex.units.option.IMPERIAL = Angloamerikanisches System
-thing-type.config.bmwconnecteddrive.bev_rex.units.option.METRIC = Metrisches System
-thing-type.config.bmwconnecteddrive.bev_rex.imageSize.label = Bildgröße
-thing-type.config.bmwconnecteddrive.bev_rex.imageSize.description = Bildgröße des Fahrzeugs für Länge und Breite
-thing-type.config.bmwconnecteddrive.bev_rex.imageViewport.label = Bild Ansicht
-thing-type.config.bmwconnecteddrive.bev_rex.imageViewport.description = Ansicht des Fahrzeugs
-thing-type.config.bmwconnecteddrive.bev_rex.imageViewport.option.FRONT = Vorderansicht
-thing-type.config.bmwconnecteddrive.bev_rex.imageViewport.option.REAR = Rückansicht
-thing-type.config.bmwconnecteddrive.bev_rex.imageViewport.option.SIDE = Seitenansicht
-thing-type.config.bmwconnecteddrive.bev_rex.imageViewport.option.DASHBOARD = Innenansicht Armaturen
-thing-type.config.bmwconnecteddrive.bev_rex.imageViewport.option.DRIVERDOOR = Seitenansicht Fahrertür
-
-thing-type.bmwconnecteddrive.bev.label = Elektrofahrzeug
-thing-type.bmwconnecteddrive.bev.description = Batterieelektrisches Fahrzeug (bev)
-thing-type.config.bmwconnecteddrive.bev.vin.label = Fahrzeug Identifikationsnummer (VIN)
-thing-type.config.bmwconnecteddrive.bev.vin.description = VIN des Fahrzeugs
-thing-type.config.bmwconnecteddrive.bev.refreshInterval.label = Datenaktualisierung in Minuten
-thing-type.config.bmwconnecteddrive.bev.refreshInterval.description = Rate der Datenaktualisierung Ihres Fahrzeugs
-thing-type.config.bmwconnecteddrive.bev.units.label = Einheiten
-thing-type.config.bmwconnecteddrive.bev.units.description = Automatische oder direkte Auswahl der Einheiten
-thing-type.config.bmwconnecteddrive.bev.units.option.AUTODETECT = Automatische Auswahl
-thing-type.config.bmwconnecteddrive.bev.units.option.IMPERIAL = Angloamerikanisches System
-thing-type.config.bmwconnecteddrive.bev.units.option.METRIC = Metrisches System
-thing-type.config.bmwconnecteddrive.bev.imageSize.label = Bildgröße
-thing-type.config.bmwconnecteddrive.bev.imageSize.description = Bildgröße des Fahrzeugs für Länge und Breite
-thing-type.config.bmwconnecteddrive.bev.imageViewport.label = Bild Ansicht
-thing-type.config.bmwconnecteddrive.bev.imageViewport.description = Ansicht des Fahrzeugs
-thing-type.config.bmwconnecteddrive.bev.imageViewport.option.FRONT = Vorderansicht
-thing-type.config.bmwconnecteddrive.bev.imageViewport.option.REAR = Rückansicht
-thing-type.config.bmwconnecteddrive.bev.imageViewport.option.SIDE = Seitenansicht
-thing-type.config.bmwconnecteddrive.bev.imageViewport.option.DASHBOARD = Innenansicht Armaturen
-thing-type.config.bmwconnecteddrive.bev.imageViewport.option.DRIVERDOOR = Seitenansicht Fahrertür
-
-thing-type.bmwconnecteddrive.phev.label = Plug-in-Hybrid Elektrofahrzeug
-thing-type.bmwconnecteddrive.phev.description = Konventionelles Fahrzeug mit Elektromotor (phev)
-thing-type.config.bmwconnecteddrive.phev.vin.label = Fahrzeug Identifikationsnummer (VIN)
-thing-type.config.bmwconnecteddrive.phev.vin.description = VIN des Fahrzeugs
-thing-type.config.bmwconnecteddrive.phev.refreshInterval.label = Datenaktualisierung in Minuten
-thing-type.config.bmwconnecteddrive.phev.refreshInterval.description = Rate der Datenaktualisierung Ihres Fahrzeugs
-thing-type.config.bmwconnecteddrive.phev.units.label = Einheiten
-thing-type.config.bmwconnecteddrive.phev.units.description = Automatische oder direkte Auswahl der Einheiten
-thing-type.config.bmwconnecteddrive.phev.units.option.AUTODETECT = Automatische Auswahl
-thing-type.config.bmwconnecteddrive.phev.units.option.IMPERIAL = Angloamerikanisches System
-thing-type.config.bmwconnecteddrive.phev.units.option.METRIC = Metrisches System
-thing-type.config.bmwconnecteddrive.phev.imageSize.label = Bildgröße
-thing-type.config.bmwconnecteddrive.phev.imageSize.description = Bildgröße des Fahrzeugs für Länge und Breite
-thing-type.config.bmwconnecteddrive.phev.imageViewport.label = Bild Ansicht
-thing-type.config.bmwconnecteddrive.phev.imageViewport.description = Ansicht des Fahrzeugs
-thing-type.config.bmwconnecteddrive.phev.imageViewport.option.FRONT = Vorderansicht
-thing-type.config.bmwconnecteddrive.phev.imageViewport.option.REAR = Rückansicht
-thing-type.config.bmwconnecteddrive.phev.imageViewport.option.SIDE = Seitenansicht
-thing-type.config.bmwconnecteddrive.phev.imageViewport.option.DASHBOARD = Innenansicht Armaturen
-thing-type.config.bmwconnecteddrive.phev.imageViewport.option.DRIVERDOOR = Seitenansicht Fahrertür
-
-thing-type.bmwconnecteddrive.conv.label = Konventionelles Fahrzeug
-thing-type.bmwconnecteddrive.conv.description = Konventionelles Benzin/Diesel Fahrzeug (conv)
-thing-type.config.bmwconnecteddrive.conv.vin.label = Fahrzeug Identifikationsnummer (VIN)
-thing-type.config.bmwconnecteddrive.conv.vin.description = VIN des Fahrzeugs
-thing-type.config.bmwconnecteddrive.conv.refreshInterval.label = Datenaktualisierung in Minuten
-thing-type.config.bmwconnecteddrive.conv.refreshInterval.description = Rate der Datenaktualisierung Ihres Fahrzeugs
-thing-type.config.bmwconnecteddrive.conv.units.label = Einheiten
-thing-type.config.bmwconnecteddrive.conv.units.description = Automatische oder direkte Auswahl der Einheiten
-thing-type.config.bmwconnecteddrive.conv.units.option.AUTODETECT = Automatische Auswahl
-thing-type.config.bmwconnecteddrive.conv.units.option.IMPERIAL = Angloamerikanisches System
-thing-type.config.bmwconnecteddrive.conv.units.option.METRIC = Metrisches System
-thing-type.config.bmwconnecteddrive.conv.imageSize.label = Bildgröße
-thing-type.config.bmwconnecteddrive.conv.imageSize.description = Bildgröße des Fahrzeugs für Länge und Breite
-thing-type.config.bmwconnecteddrive.conv.imageViewport.label = Bild Ansicht
-thing-type.config.bmwconnecteddrive.conv.imageViewport.description = Ansicht des Fahrzeugs
-thing-type.config.bmwconnecteddrive.conv.imageViewport.option.FRONT = Vorderansicht
-thing-type.config.bmwconnecteddrive.conv.imageViewport.option.REAR = Rückansicht
-thing-type.config.bmwconnecteddrive.conv.imageViewport.option.SIDE = Seitenansicht
-thing-type.config.bmwconnecteddrive.conv.imageViewport.option.DASHBOARD = Innenansicht Armaturen
-thing-type.config.bmwconnecteddrive.conv.imageViewport.option.DRIVERDOOR = Seitenansicht Fahrertür
-
-# Channel Groups
-channel-group-type.bmwconnecteddrive.charge-values.label = Elektrisches Laden
-channel-group-type.bmwconnecteddrive.charge-values.description = Ladezustand und Ladeprofile des Fahrzeugs
-channel-group-type.bmwconnecteddrive.ev-lifetime-values.label = Gesamtlaufzeit Statistik
-channel-group-type.bmwconnecteddrive.ev-lifetime-values.description = Verbrauchswerte und zurückgelegte Strecken über die Fahrzeug-Gesamtlaufzeit
-channel-group-type.bmwconnecteddrive.hybrid-lifetime-values.label = Gesamtlaufzeit Statistik
-channel-group-type.bmwconnecteddrive.hybrid-lifetime-values.description = Verbrauchswerte und zurückgelegte Strecken über die Fahrzeug-Gesamtlaufzeit
-channel-group-type.bmwconnecteddrive.ev-last-trip-values.label = Statistik der letzten Fahrt
-channel-group-type.bmwconnecteddrive.ev-last-trip-values.description = Verbrauchswerte und zurück gelegte Strecke der letzten Fahrt
-channel-group-type.bmwconnecteddrive.hybrid-last-trip-values.label = Statistik der letzten Fahrt
-channel-group-type.bmwconnecteddrive.hybrid-last-trip-values.description = Verbrauchswerte und zurück gelegte Strecke der letzten Fahrt
-channel-group-type.bmwconnecteddrive.ev-range-values.label = Elektrische Reichweite
-channel-group-type.bmwconnecteddrive.ev-range-values.description = Tachostand, Reichweiten und Ladestand des Fahrzeugs
-channel-group-type.bmwconnecteddrive.check-control-values.label = Warnungen
-channel-group-type.bmwconnecteddrive.check-control-values.description = Aktuelle Warungen des Fahrzeugs
-channel-group-type.bmwconnecteddrive.service-values.label = Wartung
-channel-group-type.bmwconnecteddrive.service-values.description = Zukünftige Wartungstermine des Fahrzeugs
-channel-group-type.bmwconnecteddrive.conv-range-values.label = Reichweite
-channel-group-type.bmwconnecteddrive.conv-range-values.description = Tachostand, Reichweite und Tankfüllung des Fahrzeugs
-channel-group-type.bmwconnecteddrive.hybrid-range-values.label = Hybride Reichweite
-channel-group-type.bmwconnecteddrive.hybrid-range-values.description = Tachostand, Reichweite, Ladezustand und Tankfüllung des Fahrzeugs
-channel-group-type.bmwconnecteddrive.image-values.label = Fahrzeug Bild
-channel-group-type.bmwconnecteddrive.image-values.description = Bild des Fahrzeug basierend auf der Ansicht in der Konfiguration
-channel-group-type.bmwconnecteddrive.remote-services.label = Fahrzeug Fernsteuerung
-channel-group-type.bmwconnecteddrive.remote-services.description = Fernsteuerung des Fahrzeugs über den BMW Server wie Türen schließen / öffnen, Klimasteuerung und mehr
-channel-group-type.bmwconnecteddrive.vehicle-status.label = Fahrzeug Zustand
-channel-group-type.bmwconnecteddrive.vehicle-status.description = Zustand des Fahrzeugs über Türen, Fenster, abgeschlossen, anstehende Wartung und aktive Warnungen
-channel-group-type.bmwconnecteddrive.ev-vehicle-status.label = Fahrzeug Zustand
-channel-group-type.bmwconnecteddrive.ev-vehicle-status.description = Zustand des Fahrzeugs über Türen, Fenster, abgeschlossen, anstehende Wartung und aktive Warnungen
-channel-group-type.bmwconnecteddrive.location-values.label = Fahrzeug Standort
-channel-group-type.bmwconnecteddrive.location-values.description = Koordinaten und Ausrichtung des Fahrzeugs
-channel-group-type.bmwconnecteddrive.destination-values.label = Ziele
-channel-group-type.bmwconnecteddrive.destination-values.description = Zeigt die gespeicherten Ziele des Fahrzeugs
-channel-group-type.bmwconnecteddrive.troubleshoot-control.label = Fehlerbehebung
-channel-group-type.bmwconnecteddrive.troubleshoot-control.description = Generiert Daten zur Fehlerbehebung eines Problems
-channel-group-type.bmwconnecteddrive.door-values.label = Details aller Türen
-channel-group-type.bmwconnecteddrive.door-values.description = Zeigt die Details der Türen und Fenster des Fahrzeugs
-
-# Channel Types
-channel-type.bmwconnecteddrive.doors-channel.label = Gesamtzustand der Türen
-channel-type.bmwconnecteddrive.windows-channel.label = Gesamtzustand der Fenster
-channel-type.bmwconnecteddrive.lock-channel.label = Fahrzeug Abgeschlossen
-channel-type.bmwconnecteddrive.next-service-date-channel.label = Nächster Service Termin
-channel-type.bmwconnecteddrive.next-service-mileage-channel.label = Nächster Service in Kilometern
-channel-type.bmwconnecteddrive.check-control-channel.label = Warnung Aktiv
-channel-type.bmwconnecteddrive.charging-status-channel.label = Ladezustand
-channel-type.bmwconnecteddrive.plug-connection-channel.label = Ladestecker
-channel-type.bmwconnecteddrive.charging-remaining-channel.label = Verbleibende Ladezeit
-channel-type.bmwconnecteddrive.last-update-channel.label = Letzte Aktualisierung
-channel-type.bmwconnecteddrive.last-update-reason-channel.label = Grund der letzten Aktualisierung
-
-channel-type.bmwconnecteddrive.driver-front-channel.label = Fahrertür
-channel-type.bmwconnecteddrive.driver-rear-channel.label = Fahrertür Hinten
-channel-type.bmwconnecteddrive.passenger-front-channel.label = Beifahrertür
-channel-type.bmwconnecteddrive.passenger-rear-channel.label = Beifahrertür Hinten
-channel-type.bmwconnecteddrive.hood-channel.label = Frontklappe
-channel-type.bmwconnecteddrive.trunk-channel.label = Heckklappe
-channel-type.bmwconnecteddrive.window-driver-front-channel.label = Fahrertür Fenster
-channel-type.bmwconnecteddrive.window-driver-rear-channel.label = Fahrertür Hinten Fenster
-channel-type.bmwconnecteddrive.window-passenger-front-channel.label = Beifahrertür Fenster
-channel-type.bmwconnecteddrive.window-passenger-rear-channel.label = Beifahrertür Hinten Fenster
-channel-type.bmwconnecteddrive.window-rear-channel.label = Heckfenster
-channel-type.bmwconnecteddrive.sunroof-channel.label = Schiebedach
-
-channel-type.bmwconnecteddrive.mileage-channel.label = Tachostand
-channel-type.bmwconnecteddrive.range-hybrid-channel.label = Hybride Reichweite
-channel-type.bmwconnecteddrive.range-hybrid-max-channel.label = Hybride Reichweite bei voller Ladung
-channel-type.bmwconnecteddrive.range-electric-channel.label = Elektrische Reichweite
-channel-type.bmwconnecteddrive.range-electric-max-channel.label = Elektrische Reichweite bei voller Ladung
-channel-type.bmwconnecteddrive.soc-channel.label = Batterie Ladestand
-channel-type.bmwconnecteddrive.soc-max-channel.label = Maximale Batteriekapazität
-channel-type.bmwconnecteddrive.range-fuel-channel.label = Verbrenner Reichweite
-channel-type.bmwconnecteddrive.remaining-fuel-channel.label = Tankstand
-channel-type.bmwconnecteddrive.range-radius-electric-channel.label = Elektrischer Reichweiten-Radius
-channel-type.bmwconnecteddrive.range-radius-electric-max-channel.label = Elektrischer Reichweiten-Radius bei voller Ladung
-channel-type.bmwconnecteddrive.range-radius-fuel-channel.label = Verbrenner Reichweiten-Radius
-channel-type.bmwconnecteddrive.range-radius-hybrid-channel.label = Hybrider Reichweiten-Radius
-channel-type.bmwconnecteddrive.range-radius-hybrid-max-channel.label = Hybrider Reichweiten-Radius bei voller Ladung
-
-channel-type.bmwconnecteddrive.service-name-channel.label = Service
-channel-type.bmwconnecteddrive.service-details-channel.label = Service Details
-channel-type.bmwconnecteddrive.service-date-channel.label = Service Termin
-channel-type.bmwconnecteddrive.service-mileage-channel.label = Service in Kilometern
-
-channel-type.bmwconnecteddrive.checkcontrol-name-channel.label = Warnung
-channel-type.bmwconnecteddrive.checkcontrol-details-channel.label = Warnung Details
-channel-type.bmwconnecteddrive.checkcontrol-mileage-channel.label = Warnung bei Kilometer
-
-channel-type.bmwconnecteddrive.profile-climate-channel.label = Klimatisierung bei Abfahrt
-channel-type.bmwconnecteddrive.profile-mode-channel.label = Ladeprofil
-channel-type.bmwconnecteddrive.profile-mode-channel.option.IMMEDIATE_CHARGING = Sofortiges Laden
-channel-type.bmwconnecteddrive.profile-mode-channel.option.DELAYED_CHARGING = Laden im Zeitfenster
-channel-type.bmwconnecteddrive.profile-prefs-channel.label = Ladeprofil Präferenz
-channel-type.bmwconnecteddrive.profile-prefs-channel.option.NO_PRESELECTION = Keine Präferenz
-channel-type.bmwconnecteddrive.profile-prefs-channel.option.Charging\ Window = Zeitfenster
-channel-type.bmwconnecteddrive.window-start-channel.label = Ladefenster Startzeit
-channel-type.bmwconnecteddrive.window-start-hour-channel.label = Ladefenster Startzeit Stunde
-channel-type.bmwconnecteddrive.window-start-minute-channel.label = Ladefenster Startzeit Minute
-channel-type.bmwconnecteddrive.window-end-channel.label = Ladefenster Endzeit
-channel-type.bmwconnecteddrive.window-end-hour-channel.label = Ladefenster Endzeit Stunde
-channel-type.bmwconnecteddrive.window-end-minute-channel.label = Ladefenster Endzeit Minute
-channel-type.bmwconnecteddrive.timer1-enabled-channel.label = Zeitprofil 1 - Aktiviert
-channel-type.bmwconnecteddrive.timer1-departure-channel.label = Zeitprofil 1 - Abfahrtszeit
-channel-type.bmwconnecteddrive.timer1-departure-hour-channel.label = Zeitprofil 1 - Abfahrtszeit Stunde
-channel-type.bmwconnecteddrive.timer1-departure-minute-channel.label = Zeitprofil 1 - Abfahrtszeit Minute
-channel-type.bmwconnecteddrive.timer1-days-channel.label = Zeitprofil 1 - Tage
-channel-type.bmwconnecteddrive.timer1-day-mon-channel.label = Zeitprofil 1 - Montag
-channel-type.bmwconnecteddrive.timer1-day-tue-channel.label = Zeitprofil 1 - Dienstag
-channel-type.bmwconnecteddrive.timer1-day-wed-channel.label = Zeitprofil 1 - Mittwoch
-channel-type.bmwconnecteddrive.timer1-day-thu-channel.label = Zeitprofil 1 - Donnerstag
-channel-type.bmwconnecteddrive.timer1-day-fri-channel.label = Zeitprofil 1 - Freitag
-channel-type.bmwconnecteddrive.timer1-day-sat-channel.label = Zeitprofil 1 - Samstag
-channel-type.bmwconnecteddrive.timer1-day-sun-channel.label = Zeitprofil 1 - Sonntag
-channel-type.bmwconnecteddrive.timer2-enabled-channel.label = Zeitprofil 2 - Aktiviert
-channel-type.bmwconnecteddrive.timer2-departure-channel.label = Zeitprofil 2 - Abfahrtszeit
-channel-type.bmwconnecteddrive.timer2-departure-hour-channel.label = Zeitprofil 2 - Abfahrtszeit Stunde
-channel-type.bmwconnecteddrive.timer2-departure-minute-channel.label = Zeitprofil 2 - Abfahrtszeit Minute
-channel-type.bmwconnecteddrive.timer2-days-channel.label = Zeitprofil 2 - Tage
-channel-type.bmwconnecteddrive.timer2-day-mon-channel.label = Zeitprofil 2 - Montag
-channel-type.bmwconnecteddrive.timer2-day-tue-channel.label = Zeitprofil 2 - Dienstag
-channel-type.bmwconnecteddrive.timer2-day-wed-channel.label = Zeitprofil 2 - Mittwoch
-channel-type.bmwconnecteddrive.timer2-day-thu-channel.label = Zeitprofil 2 - Donnerstag
-channel-type.bmwconnecteddrive.timer2-day-fri-channel.label = Zeitprofil 2 - Freitag
-channel-type.bmwconnecteddrive.timer2-day-sat-channel.label = Zeitprofil 2 - Samstag
-channel-type.bmwconnecteddrive.timer2-day-sun-channel.label = Zeitprofil 2 - Sonnatg
-channel-type.bmwconnecteddrive.timer3-enabled-channel.label = Zeitprofil 3 - Aktiviert
-channel-type.bmwconnecteddrive.timer3-departure-channel.label = Zeitprofil 3 - Abfahrtszeit
-channel-type.bmwconnecteddrive.timer3-departure-hour-channel.label = Zeitprofil 3 - Abfahrtszeit Stunde
-channel-type.bmwconnecteddrive.timer3-departure-minute-channel.label = Zeitprofil 3 - Abfahrtszeit Minute
-channel-type.bmwconnecteddrive.timer3-days-channel.label = Zeitprofil 3 - Tage
-channel-type.bmwconnecteddrive.timer3-day-mon-channel.label = Zeitprofil 3 - Montag
-channel-type.bmwconnecteddrive.timer3-day-tue-channel.label = Zeitprofil 3 - Dienstag
-channel-type.bmwconnecteddrive.timer3-day-wed-channel.label = Zeitprofil 3 - Mittwoch
-channel-type.bmwconnecteddrive.timer3-day-thu-channel.label = Zeitprofil 3 - Donnerstag
-channel-type.bmwconnecteddrive.timer3-day-fri-channel.label = Zeitprofil 3 - Freitag
-channel-type.bmwconnecteddrive.timer3-day-sat-channel.label = Zeitprofil 3 - Samstag
-channel-type.bmwconnecteddrive.timer3-day-sun-channel.label = Zeitprofil 3 - Sonntag
-channel-type.bmwconnecteddrive.override-departure-channel.label = Einmaliges Zeitprofil - Abfahrtszeit
-channel-type.bmwconnecteddrive.override-departure-hour-channel.label = Einmaliges Zeitprofil - Abfahrtszeit Stunde
-channel-type.bmwconnecteddrive.override-departure-minute-channel.label = Einmaliges Zeitprofil - Abfahrtszeit Minute
-channel-type.bmwconnecteddrive.override-enabled-channel.label = Einmaliges Zeitprofil - Aktiviert
-
-channel-type.bmwconnecteddrive.destination-name-channel.label = Zieladresse
-channel-type.bmwconnecteddrive.destination-gps-channel.label = Zielkoordinaten
-
-channel-type.bmwconnecteddrive.gps-channel.label = Koordinaten
-channel-type.bmwconnecteddrive.heading-channel.label = Ausrichtung
-
-channel-type.bmwconnecteddrive.trip-date-time-channel.label = Datum
-channel-type.bmwconnecteddrive.trip-duration-channel.label = Dauer
-channel-type.bmwconnecteddrive.distance-channel.label = Distanz
-channel-type.bmwconnecteddrive.distance-since-charging-channel.label = Strecke seit Ladung
-channel-type.bmwconnecteddrive.average-consumption-channel.label = Elektrischer Verbrauch
-channel-type.bmwconnecteddrive.average-consumption-channel.description = Elektrischer Durchnittsverbaruch über 100 km/mi
-channel-type.bmwconnecteddrive.average-combined-consumption-channel.label = Kombinierter Verbrauch
-channel-type.bmwconnecteddrive.average-combined-consumption-channel.description = Kombinierter Durchnittsverbaruch in Liter über 100 km/mi
-channel-type.bmwconnecteddrive.average-recuperation-channel.label = Rekuperation Durchschnitt
-channel-type.bmwconnecteddrive.average-recuperation-channel.description = Durchschnittliche Rekuperation über 100 km/mi
-channel-type.bmwconnecteddrive.total-driven-distance-channel.label = Elektrisch gefahrene Distanz
-channel-type.bmwconnecteddrive.single-longest-distance-channel.label = Längste Fahrt mit einer Ladung
-
-channel-type.bmwconnecteddrive.remote-command-channel.label = Kommando Auswahl
-channel-type.bmwconnecteddrive.remote-command-channel.option.light = Lichthupe Ausführen
-channel-type.bmwconnecteddrive.remote-command-channel.option.finder = Fahrzeug Lokalisieren
-channel-type.bmwconnecteddrive.remote-command-channel.option.lock = Fahrzeug Abschließen
-channel-type.bmwconnecteddrive.remote-command-channel.option.unlock = Fahrzug Aufschließen
-channel-type.bmwconnecteddrive.remote-command-channel.option.horn = Hupe Aktivieren
-channel-type.bmwconnecteddrive.remote-command-channel.option.climate = Klimatisierung Ausführen
-channel-type.bmwconnecteddrive.remote-state-channel.label = Ausführungszustand
-
-
-channel-type.bmwconnecteddrive.png-channel.label = Fahrzeug Bild
-channel-type.bmwconnecteddrive.image-view-channel.label = Fahrzeug Ansicht
-channel-type.bmwconnecteddrive.image-size-channel.label = Fahrzeug Bildgröße
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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">
-
- <bridge-type id="account">
- <label>BMW ConnectedDrive Account</label>
- <description>Access to BMW ConnectedDrive Portal for a specific user</description>
- <config-description-ref uri="thing-type:bmwconnecteddrive:bridge"/>
- </bridge-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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>Electric Charging</label>
- <description>Charge Profiles of Vehicle</description>
- <channels>
- <channel id="profile-climate" typeId="profile-climate-channel"/>
- <channel id="profile-mode" typeId="profile-mode-channel"/>
- <channel id="profile-prefs" typeId="profile-prefs-channel"/>
- <channel id="window-start" typeId="window-start-channel"/>
- <channel id="window-end" typeId="window-end-channel"/>
- <channel id="timer1-departure" typeId="timer1-departure-channel"/>
- <channel id="timer1-days" typeId="timer1-days-channel"/>
- <channel id="timer1-day-mon" typeId="timer1-day-mon-channel"/>
- <channel id="timer1-day-tue" typeId="timer1-day-tue-channel"/>
- <channel id="timer1-day-wed" typeId="timer1-day-wed-channel"/>
- <channel id="timer1-day-thu" typeId="timer1-day-thu-channel"/>
- <channel id="timer1-day-fri" typeId="timer1-day-fri-channel"/>
- <channel id="timer1-day-sat" typeId="timer1-day-sat-channel"/>
- <channel id="timer1-day-sun" typeId="timer1-day-sun-channel"/>
- <channel id="timer1-enabled" typeId="timer1-enabled-channel"/>
- <channel id="timer2-departure" typeId="timer2-departure-channel"/>
- <channel id="timer2-days" typeId="timer2-days-channel"/>
- <channel id="timer2-day-mon" typeId="timer2-day-mon-channel"/>
- <channel id="timer2-day-tue" typeId="timer2-day-tue-channel"/>
- <channel id="timer2-day-wed" typeId="timer2-day-wed-channel"/>
- <channel id="timer2-day-thu" typeId="timer2-day-thu-channel"/>
- <channel id="timer2-day-fri" typeId="timer2-day-fri-channel"/>
- <channel id="timer2-day-sat" typeId="timer2-day-sat-channel"/>
- <channel id="timer2-day-sun" typeId="timer2-day-sun-channel"/>
- <channel id="timer2-enabled" typeId="timer2-enabled-channel"/>
- <channel id="timer3-departure" typeId="timer3-departure-channel"/>
- <channel id="timer3-days" typeId="timer3-days-channel"/>
- <channel id="timer3-day-mon" typeId="timer3-day-mon-channel"/>
- <channel id="timer3-day-tue" typeId="timer3-day-tue-channel"/>
- <channel id="timer3-day-wed" typeId="timer3-day-wed-channel"/>
- <channel id="timer3-day-thu" typeId="timer3-day-thu-channel"/>
- <channel id="timer3-day-fri" typeId="timer3-day-fri-channel"/>
- <channel id="timer3-day-sat" typeId="timer3-day-sat-channel"/>
- <channel id="timer3-day-sun" typeId="timer3-day-sun-channel"/>
- <channel id="timer3-enabled" typeId="timer3-enabled-channel"/>
- <channel id="override-departure" typeId="override-departure-channel"/>
- <channel id="override-enabled" typeId="override-enabled-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="profile-climate-channel">
- <item-type>Switch</item-type>
- <label>A/C at Departure Time</label>
- </channel-type>
- <channel-type id="profile-mode-channel">
- <item-type>String</item-type>
- <label>Charge Mode</label>
- <description>Mode for selecting immediate or delyed charging</description>
- <command>
- <options>
- <option value="IMMEDIATE_CHARGING">Immediate Charging</option>
- <option value="DELAYED_CHARGING">Prefer Charging in Charging Window</option>
- </options>
- </command>
- </channel-type>
- <channel-type id="profile-prefs-channel">
- <item-type>String</item-type>
- <label>Charge Preferences</label>
- <description>Preferences for delayed charging</description>
- <command>
- <options>
- <option value="NO_PRESELECTION">No Preference</option>
- <option value="CHARGING_WINDOW">Charging Window</option>
- </options>
- </command>
- </channel-type>
- <channel-type id="window-start-channel">
- <item-type>DateTime</item-type>
- <label>Window Start Time</label>
- <description>Start time of charging window</description>
- <state pattern="%1$tH:%1$tM" readOnly="false"/>
- </channel-type>
- <channel-type id="window-end-channel">
- <item-type>DateTime</item-type>
- <label>Window End Time</label>
- <description>End time of charging window</description>
- <state pattern="%1$tH:%1$tM" readOnly="false"/>
- </channel-type>
- <channel-type id="timer1-departure-channel">
- <item-type>DateTime</item-type>
- <label>T1 Departure Time</label>
- <description>Departure time for regular schedule timer 1</description>
- <state pattern="%1$tH:%1$tM" readOnly="false"/>
- </channel-type>
- <channel-type id="timer1-days-channel">
- <item-type>String</item-type>
- <label>T1 Days</label>
- <description>Days scheduled for timer 1</description>
- </channel-type>
- <channel-type id="timer1-day-mon-channel">
- <item-type>Switch</item-type>
- <label>T1 Monday</label>
- <description>Monday scheduled for timer 1</description>
- </channel-type>
- <channel-type id="timer1-day-tue-channel">
- <item-type>Switch</item-type>
- <label>T1 Tuesday</label>
- <description>Tuesday scheduled for timer 1</description>
- </channel-type>
- <channel-type id="timer1-day-wed-channel">
- <item-type>Switch</item-type>
- <label>T1 Wednesday</label>
- <description>Wednesday scheduled for timer 1</description>
- </channel-type>
- <channel-type id="timer1-day-thu-channel">
- <item-type>Switch</item-type>
- <label>T1 Thursday</label>
- <description>Thursday scheduled for timer 1</description>
- </channel-type>
- <channel-type id="timer1-day-fri-channel">
- <item-type>Switch</item-type>
- <label>T1 Friday</label>
- <description>Friday scheduled for timer 1</description>
- </channel-type>
- <channel-type id="timer1-day-sat-channel">
- <item-type>Switch</item-type>
- <label>T1 Saturday</label>
- <description>Saturday scheduled for timer 1</description>
- </channel-type>
- <channel-type id="timer1-day-sun-channel">
- <item-type>Switch</item-type>
- <label>T1 Sunday</label>
- <description>Sunday scheduled for timer 1</description>
- </channel-type>
- <channel-type id="timer1-enabled-channel">
- <item-type>Switch</item-type>
- <label>T1 Enabled</label>
- <description>Timer 1 enabled</description>
- </channel-type>
- <channel-type id="timer2-departure-channel">
- <item-type>DateTime</item-type>
- <label>T2 Departure Time</label>
- <description>Departure time for regular schedule timer 2</description>
- <state pattern="%1$tH:%1$tM" readOnly="false"/>
- </channel-type>
- <channel-type id="timer2-days-channel">
- <item-type>String</item-type>
- <label>T2 Days</label>
- <description>Days scheduled for timer 2</description>
- </channel-type>
- <channel-type id="timer2-day-mon-channel">
- <item-type>Switch</item-type>
- <label>T2 Monday</label>
- <description>Monday scheduled for timer 2</description>
- </channel-type>
- <channel-type id="timer2-day-tue-channel">
- <item-type>Switch</item-type>
- <label>T2 Tuesday</label>
- <description>Tuesday scheduled for timer 2</description>
- </channel-type>
- <channel-type id="timer2-day-wed-channel">
- <item-type>Switch</item-type>
- <label>T2 Wednesday</label>
- <description>Wednesday scheduled for timer 2</description>
- </channel-type>
- <channel-type id="timer2-day-thu-channel">
- <item-type>Switch</item-type>
- <label>T2 Thursday</label>
- <description>Thursday scheduled for timer 2</description>
- </channel-type>
- <channel-type id="timer2-day-fri-channel">
- <item-type>Switch</item-type>
- <label>T2 Friday</label>
- <description>Friday scheduled for timer 2</description>
- </channel-type>
- <channel-type id="timer2-day-sat-channel">
- <item-type>Switch</item-type>
- <label>T2 Saturday</label>
- <description>Saturday scheduled for timer 2</description>
- </channel-type>
- <channel-type id="timer2-day-sun-channel">
- <item-type>Switch</item-type>
- <label>T2 Sunday</label>
- <description>Sunday scheduled for timer 2</description>
- </channel-type>
- <channel-type id="timer2-enabled-channel">
- <item-type>Switch</item-type>
- <label>T2 Enabled</label>
- <description>Timer 2 enabled</description>
- </channel-type>
- <channel-type id="timer3-departure-channel">
- <item-type>DateTime</item-type>
- <label>T3 Departure Time</label>
- <description>Departure time for regular schedule timer 3</description>
- <state pattern="%1$tH:%1$tM" readOnly="false"/>
- </channel-type>
- <channel-type id="timer3-days-channel">
- <item-type>String</item-type>
- <label>T3 Days</label>
- <description>Days scheduled for timer 3</description>
- </channel-type>
- <channel-type id="timer3-day-mon-channel">
- <item-type>Switch</item-type>
- <label>T3 Monday</label>
- <description>Monday scheduled for timer 3</description>
- </channel-type>
- <channel-type id="timer3-day-tue-channel">
- <item-type>Switch</item-type>
- <label>T3 Tuesday</label>
- <description>Tuesday scheduled for timer 3</description>
- </channel-type>
- <channel-type id="timer3-day-wed-channel">
- <item-type>Switch</item-type>
- <label>T3 Wednesday</label>
- <description>Wednesday scheduled for timer 3</description>
- </channel-type>
- <channel-type id="timer3-day-thu-channel">
- <item-type>Switch</item-type>
- <label>T3 Thursday</label>
- <description>Thursday scheduled for timer 3</description>
- </channel-type>
- <channel-type id="timer3-day-fri-channel">
- <item-type>Switch</item-type>
- <label>T3 Friday</label>
- <description>Friday scheduled for timer 3</description>
- </channel-type>
- <channel-type id="timer3-day-sat-channel">
- <item-type>Switch</item-type>
- <label>T3 Saturday</label>
- <description>Saturday scheduled for timer 3</description>
- </channel-type>
- <channel-type id="timer3-day-sun-channel">
- <item-type>Switch</item-type>
- <label>T3 Sunday</label>
- <description>Sunday scheduled for timer 3</description>
- </channel-type>
- <channel-type id="timer3-enabled-channel">
- <item-type>Switch</item-type>
- <label>T3 Enabled</label>
- <description>Timer 3 enabled</description>
- </channel-type>
- <channel-type id="override-departure-channel">
- <item-type>DateTime</item-type>
- <label>OT Departure Time</label>
- <description>Departure time for override timer</description>
- <state pattern="%1$tH:%1$tM" readOnly="false"/>
- </channel-type>
- <channel-type id="override-enabled-channel">
- <item-type>Switch</item-type>
- <label>OT Enabled</label>
- <description>Override timer enabled</description>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="checkcontrol-name-channel">
- <item-type>String</item-type>
- <label>CheckControl Description</label>
- </channel-type>
- <channel-type id="checkcontrol-details-channel">
- <item-type>String</item-type>
- <label>CheckControl Details</label>
- </channel-type>
- <channel-type id="checkcontrol-mileage-channel">
- <item-type>Number:Length</item-type>
- <label>Mileage Occurrence</label>
- <state pattern="%d %unit%"/>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="check-control-values">
- <label>Check Control Messages</label>
- <description>Show the current active CheckControl Messages</description>
- <channels>
- <channel id="name" typeId="checkcontrol-name-channel"/>
- <channel id="details" typeId="checkcontrol-details-channel"/>
- <channel id="mileage" typeId="checkcontrol-mileage-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="conv-range-values">
- <label>Range Data</label>
- <description>Provides Mileage, remaining range and fuel level values</description>
- <channels>
- <channel id="mileage" typeId="mileage-channel"/>
- <channel id="fuel" typeId="range-fuel-channel"/>
- <channel id="remaining-fuel" typeId="remaining-fuel-channel"/>
- <channel id="radius-fuel" typeId="range-radius-fuel-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="destination-name-channel">
- <item-type>String</item-type>
- <label>Name</label>
- </channel-type>
- <channel-type id="destination-gps-channel">
- <item-type>Location</item-type>
- <label>GPS Coordinates</label>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="destination-values">
- <label>Destination List</label>
- <description>Shows Your Destinations in a List</description>
- <channels>
- <channel id="name" typeId="destination-name-channel"/>
- <channel id="gps" typeId="destination-gps-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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">
- <item-type>String</item-type>
- <label>Driver Door</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="driver-rear-channel">
- <item-type>String</item-type>
- <label>Driver Door Rear</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="passenger-front-channel">
- <item-type>String</item-type>
- <label>Passenger Door</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="passenger-rear-channel">
- <item-type>String</item-type>
- <label>Passenger Door Rear</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="hood-channel">
- <item-type>String</item-type>
- <label>Hood</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="trunk-channel">
- <item-type>String</item-type>
- <label>Trunk</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="window-driver-front-channel">
- <item-type>String</item-type>
- <label>Driver Window</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="window-driver-rear-channel">
- <item-type>String</item-type>
- <label>Driver Rear Window</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="window-passenger-front-channel">
- <item-type>String</item-type>
- <label>Passenger Window</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="window-passenger-rear-channel">
- <item-type>String</item-type>
- <label>Passenger Rear Window</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="window-rear-channel">
- <item-type>String</item-type>
- <label>Rear Window</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="sunroof-channel">
- <item-type>String</item-type>
- <label>Sunroof</label>
- <state readOnly="true"/>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="door-values">
- <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="hood" typeId="hood-channel"/>
- <channel id="trunk" typeId="trunk-channel"/>
- <channel id="win-driver-front" typeId="window-driver-front-channel"/>
- <channel id="win-driver-rear" typeId="window-driver-rear-channel"/>
- <channel id="win-passenger-front" typeId="window-passenger-front-channel"/>
- <channel id="win-passenger-rear" typeId="window-passenger-rear-channel"/>
- <channel id="win-rear" typeId="window-rear-channel"/>
- <channel id="sunroof" typeId="sunroof-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="ev-last-trip-values">
- <label>Last Trip Statistics</label>
- <description>EV Consumption Values and Distances for the Last Trip</description>
- <channels>
- <channel id="date" typeId="trip-date-time-channel"/>
- <channel id="duration" typeId="trip-duration-channel"/>
- <channel id="distance" typeId="distance-channel"/>
- <channel id="distance-since-charging" typeId="distance-since-charging-channel"/>
- <channel id="avg-consumption" typeId="average-consumption-channel"/>
- <channel id="avg-recuperation" typeId="average-recuperation-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="ev-lifetime-values">
- <label>Lifetime Statistics</label>
- <description>Consumption Values and Distances over Lifetime</description>
- <channels>
- <channel id="avg-consumption" typeId="average-consumption-channel"/>
- <channel id="avg-recuperation" typeId="average-recuperation-channel"/>
- <channel id="total-driven-distance" typeId="total-driven-distance-channel"/>
- <channel id="single-longest-distance" typeId="single-longest-distance-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="ev-range-values">
- <label>Electric Range Data</label>
- <description>Provides Mileage, remaining range and charge level values</description>
- <channels>
- <channel id="mileage" typeId="mileage-channel"/>
- <channel id="electric" typeId="range-electric-channel"/>
- <channel id="radius-electric" typeId="range-radius-electric-channel"/>
- <channel id="electric-max" typeId="range-electric-max-channel"/>
- <channel id="radius-electric-max" typeId="range-radius-electric-max-channel"/>
- <channel id="soc" typeId="soc-channel"/>
- <channel id="soc-max" typeId="soc-max-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="ev-vehicle-status">
- <label>Vehicle Status</label>
- <description>Provides Status of Doors, Windows, Lock State, Service and Check Control Messages</description>
- <channels>
- <channel id="doors" typeId="doors-channel"/>
- <channel id="windows" typeId="windows-channel"/>
- <channel id="lock" typeId="lock-channel"/>
- <channel id="service-date" typeId="next-service-date-channel"/>
- <channel id="service-mileage" typeId="next-service-mileage-channel"/>
- <channel id="check-control" typeId="check-control-channel"/>
- <channel id="plug-connection" typeId="plug-connection-channel"/>
- <channel id="charge" typeId="charging-status-channel"/>
- <channel id="remaining" typeId="charging-remaining-channel"/>
- <channel id="last-update" typeId="last-update-channel"/>
- <channel id="last-update-reason" typeId="last-update-reason-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="hybrid-last-trip-values">
- <label>Last Trip Statistics</label>
- <description>Hybrid Consumption Values and Distances for the Last Trip</description>
- <channels>
- <channel id="date" typeId="trip-date-time-channel"/>
- <channel id="duration" typeId="trip-duration-channel"/>
- <channel id="distance" typeId="distance-channel"/>
- <channel id="distance-since-charging" typeId="distance-since-charging-channel"/>
- <channel id="avg-consumption" typeId="average-consumption-channel"/>
- <channel id="avg-combined-consumption" typeId="average-combined-consumption-channel"/>
- <channel id="avg-recuperation" typeId="average-recuperation-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="hybrid-lifetime-values">
- <label>Lifetime Statistics</label>
- <description>Consumption Values and Distances over Lifetime</description>
- <channels>
- <channel id="avg-consumption" typeId="average-consumption-channel"/>
- <channel id="avg-combined-consumption" typeId="average-combined-consumption-channel"/>
- <channel id="avg-recuperation" typeId="average-recuperation-channel"/>
- <channel id="total-driven-distance" typeId="total-driven-distance-channel"/>
- <channel id="single-longest-distance" typeId="single-longest-distance-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="hybrid-range-values">
- <label>Hybrid Range Data</label>
- <description>Provides Mileage, remaining range and fuel and charge level values</description>
- <channels>
- <channel id="mileage" typeId="mileage-channel"/>
- <channel id="hybrid" typeId="range-hybrid-channel"/>
- <channel id="hybrid-max" typeId="range-hybrid-max-channel"/>
- <channel id="electric" typeId="range-electric-channel"/>
- <channel id="radius-electric" typeId="range-radius-electric-channel"/>
- <channel id="electric-max" typeId="range-electric-max-channel"/>
- <channel id="radius-electric-max" typeId="range-radius-electric-max-channel"/>
- <channel id="fuel" typeId="range-fuel-channel"/>
- <channel id="remaining-fuel" typeId="remaining-fuel-channel"/>
- <channel id="radius-hybrid" typeId="range-radius-hybrid-channel"/>
- <channel id="radius-hybrid-max" typeId="range-radius-hybrid-max-channel"/>
- <channel id="soc" typeId="soc-channel"/>
- <channel id="soc-max" typeId="soc-max-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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 Image</label>
- <description>Provides an Image of your Vehicle</description>
- <channels>
- <channel id="png" typeId="png-channel"/>
- <channel id="size" typeId="image-size-channel"/>
- <channel id="view" typeId="image-view-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="png-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>
- <command>
- <options>
- <option value="FRONT">Front View</option>
- <option value="REAR">Rear View</option>
- <option value="SIDE">Side View</option>
- <option value="DASHBOARD">Dashboard View</option>
- <option value="DRIVERDOOR">Driver Door View</option>
- </options>
- </command>
- </channel-type>
- <channel-type id="image-size-channel">
- <item-type>Number</item-type>
- <label>Image Size</label>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="trip-date-time-channel">
- <item-type>DateTime</item-type>
- <label>Date and Time</label>
- <state pattern="%1$tA, %1$td.%1$tm. %1$tH:%1$tM" readOnly="true"/>
- </channel-type>
- <channel-type id="trip-duration-channel">
- <item-type>Number:Time</item-type>
- <label>Last Trip Duration</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="distance-channel">
- <item-type>Number:Length</item-type>
- <label>Last Trip Distance</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="distance-since-charging-channel">
- <item-type>Number:Length</item-type>
- <label>Distance since Charge</label>
- <description>Total distance driven since last charging</description>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="average-consumption-channel">
- <item-type>Number:Energy</item-type>
- <label>Avg. Power Consumption</label>
- <description>Average electric power consumption per 100 km/mi</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="average-combined-consumption-channel">
- <item-type>Number:Volume</item-type>
- <label>Avg. Combined Consumption</label>
- <description>Average combined consumption in liter per 100 km/mi</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="average-recuperation-channel">
- <item-type>Number:Energy</item-type>
- <label>Avg. Recuperation</label>
- <description>Average electric recuperation per 100 km/mi</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="total-driven-distance-channel">
- <item-type>Number:Length</item-type>
- <label>Total Electric Distance</label>
- <state pattern="%.0f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="single-longest-distance-channel">
- <item-type>Number:Length</item-type>
- <label>Longest 1-Charge Distance</label>
- <state pattern="%.0f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="average-consumption-channel">
- <item-type>Number:Energy</item-type>
- <label>Avg. Power Consumption</label>
- <description>Average Combined Consumption electric power consumption per 100 km/mi</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="average-recuperation-channel">
- <item-type>Number:Energy</item-type>
- <label>Avg. Combined Consumption Recuperation</label>
- <description>Average electric recuperation per 100 km/mi</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="average-combined-consumption-channel">
- <item-type>Number:Volume</item-type>
- <label>Avg. Combined Consumption</label>
- <description>Average combined consumption in liter per 100 km/mi</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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">
- <label>Vehicle Location</label>
- <description>Coordinates and Heading of the Vehcile</description>
- <channels>
- <channel id="gps" typeId="gps-channel"/>
- <channel id="heading" typeId="heading-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="gps-channel">
- <item-type>Location</item-type>
- <label>GPS Coordinates</label>
- </channel-type>
- <channel-type id="heading-channel">
- <item-type>Number:Angle</item-type>
- <label>Heading Angle</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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">
- <item-type>Number:Length</item-type>
- <label>Total Distance Driven</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="range-electric-channel">
- <item-type>Number:Length</item-type>
- <label>Electric Range</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="range-electric-max-channel">
- <item-type>Number:Length</item-type>
- <label>Electric Range if Fully Charged</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="range-fuel-channel">
- <item-type>Number:Length</item-type>
- <label>Fuel Range</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="range-hybrid-channel">
- <item-type>Number:Length</item-type>
- <label>Hybrid Range</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="range-hybrid-max-channel">
- <item-type>Number:Length</item-type>
- <label>Hybrid Range if Fully Charged</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="soc-channel">
- <item-type>Number:Dimensionless</item-type>
- <label>Battery Charge Level</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="soc-max-channel">
- <item-type>Number:Power</item-type>
- <label>Max Battery Capacity</label>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="remaining-fuel-channel">
- <item-type>Number:Volume</item-type>
- <label>Remaining Fuel</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="range-radius-electric-channel">
- <item-type>Number:Length</item-type>
- <label>Electric Range Radius if Fully Charged</label>
- <state pattern="%.0f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="range-radius-electric-max-channel">
- <item-type>Number:Length</item-type>
- <label>Electric Range Radius</label>
- <state pattern="%.0f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="range-radius-fuel-channel">
- <item-type>Number:Length</item-type>
- <label>Fuel Range Radius</label>
- <state pattern="%.0f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="range-radius-hybrid-channel">
- <item-type>Number:Length</item-type>
- <label>Hybrid Range Radius</label>
- <state pattern="%.0f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="range-radius-hybrid-max-channel">
- <item-type>Number:Length</item-type>
- <label>Hybrid Range Radius if Fully Charged</label>
- <state pattern="%.0f %unit%" readOnly="true"/>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="remote-services">
- <label>Remote Services</label>
- <description>Services can be executed via BMW Server like Door lock/unlock, Air Conditioning and more</description>
- <channels>
- <channel id="command" typeId="remote-command-channel"/>
- <channel id="state" typeId="remote-state-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="remote-command-channel">
- <item-type>String</item-type>
- <label>Remote Command</label>
- </channel-type>
- <channel-type id="remote-state-channel">
- <item-type>String</item-type>
- <label>Service Execution State</label>
- <state readOnly="true"/>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="service-name-channel">
- <item-type>String</item-type>
- <label>Service Name</label>
- </channel-type>
- <channel-type id="service-details-channel">
- <item-type>String</item-type>
- <label>Service Details</label>
- </channel-type>
- <channel-type id="service-date-channel">
- <item-type>DateTime</item-type>
- <label>Service Date</label>
- <state pattern="%1$tb %1$tY"/>
- </channel-type>
- <channel-type id="service-mileage-channel">
- <item-type>Number:Length</item-type>
- <label>Mileage till Service</label>
- <state pattern="%d %unit%"/>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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>Vehicle Services</label>
- <description>All future Service schedules</description>
- <channels>
- <channel id="name" typeId="service-name-channel"/>
- <channel id="details" typeId="service-details-channel"/>
- <channel id="date" typeId="service-date-channel"/>
- <channel id="mileage" typeId="service-mileage-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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">
-
- <thing-type id="bev">
- <supported-bridge-type-refs>
- <bridge-type-ref id="account"/>
- </supported-bridge-type-refs>
-
- <label>Electric Vehicle</label>
- <description>Battery Electric Vehicle (BEV)</description>
-
- <channel-groups>
- <channel-group id="status" typeId="ev-vehicle-status"/>
- <channel-group id="doors" typeId="door-values"/>
- <channel-group id="range" typeId="ev-range-values"/>
- <channel-group id="check" typeId="check-control-values"/>
- <channel-group id="service" typeId="service-values"/>
- <channel-group id="location" typeId="location-values"/>
- <channel-group id="remote" typeId="remote-services"/>
- <channel-group id="charge" typeId="charge-values"/>
- <channel-group id="last-trip" typeId="ev-last-trip-values"/>
- <channel-group id="lifetime" typeId="ev-lifetime-values"/>
- <channel-group id="destination" typeId="destination-values"/>
- <channel-group id="image" typeId="image-values"/>
- </channel-groups>
-
- <representation-property>vin</representation-property>
-
- <config-description-ref uri="thing-type:bmwconnecteddrive:vehicle"/>
- </thing-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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">
-
- <thing-type id="bev_rex">
- <supported-bridge-type-refs>
- <bridge-type-ref id="account"/>
- </supported-bridge-type-refs>
-
- <label>Electric Vehicle with REX</label>
- <description>Battery Electric Vehicle with Range Extender (BEV_REX)</description>
-
- <channel-groups>
- <channel-group id="status" typeId="ev-vehicle-status"/>
- <channel-group id="doors" typeId="door-values"/>
- <channel-group id="range" typeId="hybrid-range-values"/>
- <channel-group id="check" typeId="check-control-values"/>
- <channel-group id="service" typeId="service-values"/>
- <channel-group id="location" typeId="location-values"/>
- <channel-group id="remote" typeId="remote-services"/>
- <channel-group id="charge" typeId="charge-values"/>
- <channel-group id="last-trip" typeId="hybrid-last-trip-values"/>
- <channel-group id="lifetime" typeId="hybrid-lifetime-values"/>
- <channel-group id="destination" typeId="destination-values"/>
- <channel-group id="image" typeId="image-values"/>
- </channel-groups>
-
- <representation-property>vin</representation-property>
-
- <config-description-ref uri="thing-type:bmwconnecteddrive:vehicle"/>
- </thing-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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">
-
- <thing-type id="conv">
- <supported-bridge-type-refs>
- <bridge-type-ref id="account"/>
- </supported-bridge-type-refs>
-
- <label>Conventional Vehicle</label>
- <description>Conventional Fuel Vehicle (CONV)</description>
-
- <channel-groups>
- <channel-group id="status" typeId="vehicle-status"/>
- <channel-group id="doors" typeId="door-values"/>
- <channel-group id="range" typeId="conv-range-values"/>
- <channel-group id="check" typeId="check-control-values"/>
- <channel-group id="service" typeId="service-values"/>
- <channel-group id="location" typeId="location-values"/>
- <channel-group id="remote" typeId="remote-services"/>
- <channel-group id="destination" typeId="destination-values"/>
- <channel-group id="image" typeId="image-values"/>
- </channel-groups>
-
- <representation-property>vin</representation-property>
-
- <config-description-ref uri="thing-type:bmwconnecteddrive:vehicle"/>
- </thing-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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">
-
- <thing-type id="phev">
- <supported-bridge-type-refs>
- <bridge-type-ref id="account"/>
- </supported-bridge-type-refs>
-
- <label>Plug-In-Hybrid Electric Vehicle</label>
- <description>Conventional Fuel Vehicle with supporting Electric Engine (PHEV)</description>
-
- <channel-groups>
- <channel-group id="status" typeId="ev-vehicle-status"/>
- <channel-group id="doors" typeId="door-values"/>
- <channel-group id="range" typeId="hybrid-range-values"/>
- <channel-group id="check" typeId="check-control-values"/>
- <channel-group id="service" typeId="service-values"/>
- <channel-group id="location" typeId="location-values"/>
- <channel-group id="remote" typeId="remote-services"/>
- <channel-group id="charge" typeId="charge-values"/>
- <channel-group id="last-trip" typeId="hybrid-last-trip-values"/>
- <channel-group id="lifetime" typeId="hybrid-lifetime-values"/>
- <channel-group id="destination" typeId="destination-values"/>
- <channel-group id="image" typeId="image-values"/>
- </channel-groups>
-
- <representation-property>vin</representation-property>
-
- <config-description-ref uri="thing-type:bmwconnecteddrive:vehicle"/>
- </thing-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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-channel">
- <item-type>String</item-type>
- <label>Overall Door Status</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="windows-channel">
- <item-type>String</item-type>
- <label>Overall Window Status</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="lock-channel">
- <item-type>String</item-type>
- <label>Doors Locked</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="next-service-date-channel">
- <item-type>DateTime</item-type>
- <label>Next Service Date</label>
- <state pattern="%1$tb %1$tY" readOnly="true"/>
- </channel-type>
- <channel-type id="next-service-mileage-channel">
- <item-type>Number:Length</item-type>
- <label>Mileage Till Next Service</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="check-control-channel">
- <item-type>String</item-type>
- <label>Check Control</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="charging-status-channel">
- <item-type>String</item-type>
- <label>Charging Status</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="plug-connection-channel">
- <item-type>String</item-type>
- <label>Plug Connection Status</label>
- <state readOnly="true"/>
- </channel-type>
- <channel-type id="charging-remaining-channel">
- <item-type>Number:Time</item-type>
- <label>Remaining Charging Time</label>
- <state pattern="%d %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="last-update-channel">
- <item-type>DateTime</item-type>
- <label>Last Status Timestamp</label>
- <state pattern="%1$tA, %1$td.%1$tm. %1$tH:%1$tM" readOnly="true"/>
- </channel-type>
- <channel-type id="last-update-reason-channel">
- <item-type>String</item-type>
- <label>Last Status Timestamp Reason</label>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="bmwconnecteddrive"
- 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="vehicle-status">
- <label>Vehicle Status</label>
- <description>Provides Status of Doors, Windows, Lock State, Service and Check Control Messages</description>
- <channels>
- <channel id="doors" typeId="doors-channel"/>
- <channel id="windows" typeId="windows-channel"/>
- <channel id="lock" typeId="lock-channel"/>
- <channel id="service-date" typeId="next-service-date-channel"/>
- <channel id="service-mileage" typeId="next-service-mileage-channel"/>
- <channel id="check-control" typeId="check-control-channel"/>
- <channel id="last-update" typeId="last-update-channel"/>
- <channel id="last-update-reason" typeId="last-update-reason-channel"/>
- </channels>
- </channel-group-type>
-</thing:thing-descriptions>
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.discovery;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.discovery.VehiclesContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.ConnectedDriveBridgeHandler;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-import org.openhab.core.config.discovery.DiscoveryListener;
-import org.openhab.core.config.discovery.DiscoveryResult;
-import org.openhab.core.config.discovery.DiscoveryService;
-import org.openhab.core.thing.Bridge;
-import org.openhab.core.thing.ThingUID;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link DiscoveryTest} Test Discovery Results
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class DiscoveryTest {
- private final Logger logger = LoggerFactory.getLogger(DiscoveryTest.class);
- private static final Gson GSON = new Gson();
- private static final int DISCOVERY_VEHICLES = 9;
-
- @Test
- public void testDiscovery() {
- String content = FileReader.readFileInString("src/test/resources/webapi/connected-drive-account-info.json");
- ConnectedDriveBridgeHandler bh = mock(ConnectedDriveBridgeHandler.class);
- Bridge b = mock(Bridge.class);
- when(bh.getThing()).thenReturn(b);
- when(b.getUID()).thenReturn(new ThingUID("bmwconnecteddrive", "account", "abc"));
- VehicleDiscovery discovery = new VehicleDiscovery();
- discovery.setThingHandler(bh);
- DiscoveryListener listener = mock(DiscoveryListener.class);
- discovery.addDiscoveryListener(listener);
- VehiclesContainer container = GSON.fromJson(content, VehiclesContainer.class);
- ArgumentCaptor<DiscoveryResult> discoveries = ArgumentCaptor.forClass(DiscoveryResult.class);
- ArgumentCaptor<DiscoveryService> services = ArgumentCaptor.forClass(DiscoveryService.class);
- if (container != null) {
- discovery.onResponse(container);
- verify(listener, times(1)).thingDiscovered(services.capture(), discoveries.capture());
- List<DiscoveryResult> results = discoveries.getAllValues();
- assertEquals(1, results.size(), "Found Vehicles");
- DiscoveryResult result = results.get(0);
- assertEquals("bmwconnecteddrive:bev_rex:abc:MY_REAL_VIN", result.getThingUID().getAsString(), "Thing UID");
- } else {
- assertTrue(false);
- }
- }
-
- @Test
- public void testBimmerDiscovery() {
- String content = FileReader.readFileInString("src/test/resources/responses/vehicles.json");
- ConnectedDriveBridgeHandler bh = mock(ConnectedDriveBridgeHandler.class);
- Bridge b = mock(Bridge.class);
- when(bh.getThing()).thenReturn(b);
- when(b.getUID()).thenReturn(new ThingUID("bmwconnecteddrive", "account", "abc"));
- VehicleDiscovery discovery = new VehicleDiscovery();
- discovery.setThingHandler(bh);
- DiscoveryListener listener = mock(DiscoveryListener.class);
- discovery.addDiscoveryListener(listener);
- VehiclesContainer container = GSON.fromJson(content, VehiclesContainer.class);
- ArgumentCaptor<DiscoveryResult> discoveries = ArgumentCaptor.forClass(DiscoveryResult.class);
- ArgumentCaptor<DiscoveryService> services = ArgumentCaptor.forClass(DiscoveryService.class);
-
- if (container != null) {
- discovery.onResponse(container);
- verify(listener, times(DISCOVERY_VEHICLES)).thingDiscovered(services.capture(), discoveries.capture());
- List<DiscoveryResult> results = discoveries.getAllValues();
- results.forEach(entry -> {
- logger.info("{}", entry.toString());
- });
- } else {
- assertTrue(false);
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.charge.ChargeProfile;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link ChargeProfileTest} Test json responses from ConnectedDrive Portal
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class ChargeProfileTest {
- private static final Gson GSON = new Gson();
-
- @Test
- public void testChargeProfile() {
- String resource1 = FileReader.readFileInString("src/test/resources/webapi/charging-profile.json");
- ChargeProfile cp = GSON.fromJson(resource1, ChargeProfile.class);
- assertNotNull(cp.weeklyPlanner);
- assertNotNull(cp.weeklyPlanner.timer1);
- assertFalse(cp.weeklyPlanner.timer1.timerEnabled);
- assertNotNull(cp.weeklyPlanner.timer1.weekdays);
- assertEquals(5, cp.weeklyPlanner.timer1.weekdays.size(), "Days");
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.discovery.Vehicle;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.discovery.VehiclesContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-import org.openhab.core.thing.ThingTypeUID;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link ConnectedDriveTest} Test json responses from ConnectedDrive Portal
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class ConnectedDriveTest {
- private static final Gson GSON = new Gson();
-
- @Test
- public void testUserInfo() {
- String resource1 = FileReader.readFileInString("src/test/resources/webapi/connected-drive-account-info.json");
- VehiclesContainer container = GSON.fromJson(resource1, VehiclesContainer.class);
- List<Vehicle> vehicles = container.vehicles;
- assertEquals(1, vehicles.size(), "Number of Vehicles");
- Vehicle v = vehicles.get(0);
- assertEquals("MY_REAL_VIN", v.vin, "VIN");
- assertEquals("i3 94 (+ REX)", v.model, "Model");
- assertEquals("BEV_REX", v.driveTrain, "DriveTrain");
- assertEquals("BMW_I", v.brand, "Brand");
- assertEquals(2017, v.yearOfConstruction, "Year of Construction");
- }
-
- @Test
- public void testChannelUID() {
- ThingTypeUID thingTypePHEV = new ThingTypeUID("bmwconnecteddrive", "plugin-hybrid-vehicle");
- assertEquals("plugin-hybrid-vehicle", thingTypePHEV.getId(), "Vehicle Type");
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link DestinationTest} Test json responses from ConnectedDrive Portal
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class DestinationTest {
- private final Logger logger = LoggerFactory.getLogger(DestinationTest.class);
- private static final Gson GSON = new Gson();
-
- @Test
- public void testDestinations() {
- String resource1 = FileReader.readFileInString("src/test/resources/webapi/destinations.json");
- DestinationContainer container = GSON.fromJson(resource1, DestinationContainer.class);
- List<Destination> destinations = container.destinations;
- assertEquals(9, destinations.size(), "Number of Vehicles");
- destinations.forEach(entry -> {
- logger.info(entry.getAddress());
- assertFalse(entry.getAddress().contains(Constants.NULL), "No Null contained");
- });
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.LastTrip;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.LastTripContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link LastTripTest} Test json responses from ConnectedDrive Portal
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class LastTripTest {
- private static final Gson GSON = new Gson();
-
- @Test
- public void testUserInfo() {
- String content = FileReader.readFileInString("src/test/resources/webapi/last-trip.json");
- LastTripContainer lt = GSON.fromJson(content, LastTripContainer.class);
- LastTrip trip = lt.lastTrip;
- assertNotNull(trip);
- assertEquals(2.0, trip.totalDistance, 0.01, "Distance Driven");
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.*;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.measure.Unit;
-import javax.measure.quantity.Length;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.VehicleType;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.AllTrips;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.AllTripsContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.ImperialUnits;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.types.State;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link LifetimeWrapper} Test json responses from ConnectedDrive Portal
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class LifetimeWrapper {
- private static final Gson GSON = new Gson();
- private static final Unit<Length> MILES = ImperialUnits.MILE;
-
- private AllTrips allTrips;
- private boolean imperial;
- private boolean isElectric;
- private boolean hasFuel;
- private boolean isHybrid;
-
- private Map<String, State> specialHandlingMap = new HashMap<String, State>();
-
- public LifetimeWrapper(String type, boolean imperial, String statusJson) {
- this.imperial = imperial;
- hasFuel = type.equals(VehicleType.CONVENTIONAL.toString()) || type.equals(VehicleType.PLUGIN_HYBRID.toString())
- || type.equals(VehicleType.ELECTRIC_REX.toString());
- isElectric = type.equals(VehicleType.PLUGIN_HYBRID.toString())
- || type.equals(VehicleType.ELECTRIC_REX.toString()) || type.equals(VehicleType.ELECTRIC.toString());
- isHybrid = hasFuel && isElectric;
- AllTripsContainer container = GSON.fromJson(statusJson, AllTripsContainer.class);
- assertNotNull(container);
- assertNotNull(container.allTrips);
- allTrips = container.allTrips;
- }
-
- /**
- * Test results auctomatically against json values
- *
- * @param channels
- * @param states
- * @return
- */
- public boolean checkResults(@Nullable List<ChannelUID> channels, @Nullable List<State> states) {
- assertNotNull(channels);
- assertNotNull(states);
- assertTrue(channels.size() == states.size(), "Same list sizes ");
- for (int i = 0; i < channels.size(); i++) {
- checkResult(channels.get(i), states.get(i));
- }
- return true;
- }
-
- /**
- * Add a specific check for a value e.g. hard coded "Upcoming Service" in order to check the right ordering
- *
- * @param specialHand
- * @return
- */
- public LifetimeWrapper append(Map<String, State> compareMap) {
- specialHandlingMap.putAll(compareMap);
- return this;
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private void checkResult(ChannelUID channelUID, State state) {
- String cUid = channelUID.getIdWithoutGroup();
- QuantityType<Length> qt;
- switch (cUid) {
- case DISTANCE_SINCE_CHARGING:
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (imperial) {
- assertEquals(MILES, qt.getUnit(), "Miles");
- assertEquals(allTrips.chargecycleRange.userCurrentChargeCycle / Converter.MILES_TO_KM_RATIO,
- qt.floatValue(), 0.1, "Distance since charging");
- } else {
- assertEquals(Constants.KILOMETRE_UNIT, qt.getUnit(), "KM");
- assertEquals(allTrips.chargecycleRange.userCurrentChargeCycle, qt.floatValue(), 0.1,
- "Distance since charging");
- }
- break;
- case SINGLE_LONGEST_DISTANCE:
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (imperial) {
- assertEquals(MILES, qt.getUnit(), "Miles");
- assertEquals(allTrips.chargecycleRange.userHigh / Converter.MILES_TO_KM_RATIO, qt.floatValue(), 0.1,
- "Longest Distance");
- } else {
- assertEquals(Constants.KILOMETRE_UNIT, qt.getUnit(), "KM");
- assertEquals(allTrips.chargecycleRange.userHigh, qt.floatValue(), 0.1, "Longest Distance");
- }
- break;
- case TOTAL_DRIVEN_DISTANCE:
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (imperial) {
- assertEquals(MILES, qt.getUnit(), "Miles");
- assertEquals(allTrips.totalElectricDistance.userTotal / Converter.MILES_TO_KM_RATIO,
- qt.floatValue(), 0.1, "Total Electric Distance");
- } else {
- assertEquals(Constants.KILOMETRE_UNIT, qt.getUnit(), "KM");
- assertEquals(allTrips.totalElectricDistance.userTotal, qt.floatValue(), 0.1,
- "Total Electric Distance");
- }
- break;
- case AVG_CONSUMPTION:
- assertTrue(isElectric, "Is Electric");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.KILOWATT_HOUR, qt.getUnit(), "kw/h");
- if (imperial) {
- assertEquals(allTrips.avgElectricConsumption.userAverage * Converter.MILES_TO_KM_RATIO,
- qt.floatValue(), 0.1, "Avg Consumption");
- } else {
- assertEquals(allTrips.avgElectricConsumption.userAverage, qt.floatValue(), 0.1, "Avg Consumption");
- }
- break;
- case AVG_RECUPERATION:
- assertTrue(isElectric, "Is Electric");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.KILOWATT_HOUR, qt.getUnit(), "kw/h");
- if (imperial) {
- assertEquals(allTrips.avgRecuperation.userAverage * Converter.MILES_TO_KM_RATIO, qt.floatValue(),
- 0.1, "Avg Recuperation");
- } else {
- assertEquals(allTrips.avgRecuperation.userAverage, qt.floatValue(), 0.1, "Avg Recuperation");
- }
- break;
- case AVG_COMBINED_CONSUMPTION:
- assertTrue(isHybrid, "Is Hybrid");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.LITRE, qt.getUnit(), "Liter");
- if (imperial) {
- assertEquals(allTrips.avgCombinedConsumption.userAverage * Converter.MILES_TO_KM_RATIO,
- qt.floatValue(), 0.1, "Avg Combined Consumption");
- } else {
- assertEquals(allTrips.avgCombinedConsumption.userAverage, qt.floatValue(), 0.1,
- "Avg Combined Consumption");
- }
- break;
- default:
- // fail in case of unknown update
- assertFalse(true, "Channel " + channelUID + " " + state + " not found");
- break;
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.remote.ExecutionStatus;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.remote.ExecutionStatusContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.RemoteServiceHandler.ExecutionState;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link RemoteStatusTest} Test json responses from ConnectedDrive Portal
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class RemoteStatusTest {
- private static final Gson GSON = new Gson();
-
- @Test
- public void testStatus() {
- String resource1 = FileReader.readFileInString("src/test/resources/webapi/remote-services/pending.json");
- ExecutionStatusContainer esc = GSON.fromJson(resource1, ExecutionStatusContainer.class);
- ExecutionStatus execStatus = esc.executionStatus;
- assertEquals(ExecutionState.PENDING.name(), execStatus.status, "Status");
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.*;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.measure.Unit;
-import javax.measure.quantity.Length;
-import javax.measure.quantity.Time;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.VehicleType;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.Doors;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatus;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatusContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.Windows;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.VehicleStatusUtils;
-import org.openhab.core.library.types.DateTimeType;
-import org.openhab.core.library.types.PointType;
-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.Units;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.types.State;
-import org.openhab.core.types.UnDefType;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link StatusWrapper} Test json responses from ConnectedDrive Portal
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class StatusWrapper {
- private static final Gson GSON = new Gson();
- private static final Unit<Length> KILOMETRE = Constants.KILOMETRE_UNIT;
- private static final double ALLOWED_MILE_CONVERSION_DEVIATION = 1.5;
- private static final double ALLOWED_KM_ROUND_DEVIATION = 0.1;
-
- private VehicleStatus vStatus;
- private boolean imperial;
- private boolean isElectric;
- private boolean hasFuel;
- private boolean isHybrid;
-
- private Map<String, State> specialHandlingMap = new HashMap<String, State>();
-
- public StatusWrapper(String type, boolean imperial, String statusJson) {
- this.imperial = imperial;
- hasFuel = type.equals(VehicleType.CONVENTIONAL.toString()) || type.equals(VehicleType.PLUGIN_HYBRID.toString())
- || type.equals(VehicleType.ELECTRIC_REX.toString());
- isElectric = type.equals(VehicleType.PLUGIN_HYBRID.toString())
- || type.equals(VehicleType.ELECTRIC_REX.toString()) || type.equals(VehicleType.ELECTRIC.toString());
- isHybrid = hasFuel && isElectric;
- VehicleStatusContainer container = GSON.fromJson(statusJson, VehicleStatusContainer.class);
- assertNotNull(container);
- assertNotNull(container.vehicleStatus);
- vStatus = container.vehicleStatus;
- }
-
- /**
- * Test results auctomatically against json values
- *
- * @param channels
- * @param states
- * @return
- */
- public boolean checkResults(@Nullable List<ChannelUID> channels, @Nullable List<State> states) {
- assertNotNull(channels);
- assertNotNull(states);
- assertTrue(channels.size() == states.size(), "Same list sizes");
- for (int i = 0; i < channels.size(); i++) {
- checkResult(channels.get(i), states.get(i));
- }
- return true;
- }
-
- /**
- * Add a specific check for a value e.g. hard coded "Upcoming Service" in order to check the right ordering
- *
- * @param specialHand
- * @return
- */
- public StatusWrapper append(Map<String, State> compareMap) {
- specialHandlingMap.putAll(compareMap);
- return this;
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private void checkResult(ChannelUID channelUID, State state) {
- String cUid = channelUID.getIdWithoutGroup();
- String gUid = channelUID.getGroupId();
- QuantityType<Length> qt;
- QuantityType<Time> qtt;
- StringType st;
- StringType wanted;
- DateTimeType dtt;
- PointType pt;
- switch (cUid) {
- case MILEAGE:
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (imperial) {
- assertEquals(ImperialUnits.MILE, qt.getUnit(), "Miles");
- } else {
- assertEquals(KILOMETRE, qt.getUnit(), "KM");
- }
- switch (gUid) {
- case CHANNEL_GROUP_RANGE:
- assertEquals(qt.intValue(), vStatus.mileage, "Mileage");
- break;
- case CHANNEL_GROUP_SERVICE:
- if (vStatus.cbsData.isEmpty()) {
- assertEquals(qt.intValue(), -1, "Service Mileage");
- } else {
- assertEquals(qt.intValue(), vStatus.cbsData.get(0).cbsRemainingMileage, "Service Mileage");
- }
- break;
- case CHANNEL_GROUP_CHECK_CONTROL:
- if (vStatus.checkControlMessages.isEmpty()) {
- assertEquals(qt.intValue(), -1, "CheckControl Mileage");
- } else {
- assertEquals(qt.intValue(), vStatus.checkControlMessages.get(0).ccmMileage,
- "CheckControl Mileage");
- }
- break;
- default:
- assertFalse(true, "Channel " + channelUID + " " + state + " not found");
- break;
- }
- break;
- case RANGE_ELECTRIC:
- assertTrue(isElectric, "Is Eelctric");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (imperial) {
- assertEquals(ImperialUnits.MILE, qt.getUnit(), "Miles");
- assertEquals(Converter.round(qt.floatValue()), Converter.round(vStatus.remainingRangeElectricMls),
- ALLOWED_MILE_CONVERSION_DEVIATION, "Mileage");
- } else {
- assertEquals(KILOMETRE, qt.getUnit(), "KM");
- assertEquals(Converter.round(qt.floatValue()), Converter.round(vStatus.remainingRangeElectric),
- ALLOWED_KM_ROUND_DEVIATION, "Mileage");
- }
- break;
- case RANGE_ELECTRIC_MAX:
- assertTrue(isElectric, "Is Eelctric");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (imperial) {
- assertEquals(ImperialUnits.MILE, qt.getUnit(), "Miles");
- assertEquals(Converter.round(qt.floatValue()), Converter.round(vStatus.maxRangeElectricMls),
- ALLOWED_MILE_CONVERSION_DEVIATION, "Mileage");
- } else {
- assertEquals(KILOMETRE, qt.getUnit(), "KM");
- assertEquals(Converter.round(qt.floatValue()), Converter.round(vStatus.maxRangeElectric),
- ALLOWED_KM_ROUND_DEVIATION, "Mileage");
- }
- break;
- case RANGE_FUEL:
- assertTrue(hasFuel, "Has Fuel");
- if (!(state instanceof UnDefType)) {
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (imperial) {
- assertEquals(ImperialUnits.MILE, qt.getUnit(), "Miles");
- assertEquals(Converter.round(qt.floatValue()), Converter.round(vStatus.remainingRangeFuelMls),
- ALLOWED_MILE_CONVERSION_DEVIATION, "Mileage");
- } else {
- assertEquals(KILOMETRE, qt.getUnit(), "KM");
- assertEquals(Converter.round(qt.floatValue()), Converter.round(vStatus.remainingRangeFuel),
- ALLOWED_KM_ROUND_DEVIATION, "Mileage");
- }
- }
- break;
- case RANGE_HYBRID:
- assertTrue(isHybrid, "Is Hybrid");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (imperial) {
- assertEquals(ImperialUnits.MILE, qt.getUnit(), "Miles");
- assertEquals(Converter.round(qt.floatValue()),
- Converter.round(vStatus.remainingRangeElectricMls + vStatus.remainingRangeFuelMls),
- ALLOWED_MILE_CONVERSION_DEVIATION, "Mileage");
- } else {
- assertEquals(KILOMETRE, qt.getUnit(), "KM");
- assertEquals(Converter.round(qt.floatValue()),
- Converter.round(vStatus.remainingRangeElectric + vStatus.remainingRangeFuel),
- ALLOWED_KM_ROUND_DEVIATION, "Mileage");
- }
- break;
- case RANGE_HYBRID_MAX:
- assertTrue(isHybrid, "Is Hybrid");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (imperial) {
- assertEquals(ImperialUnits.MILE, qt.getUnit(), "Miles");
- assertEquals(Converter.round(qt.floatValue()),
- Converter.round(vStatus.maxRangeElectricMls + vStatus.remainingRangeFuelMls),
- ALLOWED_MILE_CONVERSION_DEVIATION, "Mileage");
- } else {
- assertEquals(KILOMETRE, qt.getUnit(), "KM");
- assertEquals(Converter.round(qt.floatValue()),
- Converter.round(vStatus.maxRangeElectric + vStatus.remainingRangeFuel),
- ALLOWED_KM_ROUND_DEVIATION, "Mileage");
- }
- break;
- case REMAINING_FUEL:
- assertTrue(hasFuel, "Has Fuel");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.LITRE, qt.getUnit(), "Liter Unit");
- assertEquals(Converter.round(vStatus.remainingFuel), Converter.round(qt.floatValue()), 0.01,
- "Fuel Level");
- break;
- case SOC:
- assertTrue(isElectric, "Is Eelctric");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.PERCENT, qt.getUnit(), "Percent");
- assertEquals(Converter.round(vStatus.chargingLevelHv), Converter.round(qt.floatValue()), 0.01,
- "Charge Level");
- break;
- case SOC_MAX:
- assertTrue(isElectric, "Is Eelctric");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.KILOWATT_HOUR, qt.getUnit(), "kw/h");
- assertEquals(Converter.round(vStatus.chargingLevelHv), Converter.round(qt.floatValue()), 0.01,
- "SOC Max");
- break;
- case LOCK:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- assertEquals(Converter.toTitleCase(vStatus.doorLockState), st.toString(), "Vehicle locked");
- break;
- case DOORS:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- Doors doorState = GSON.fromJson(GSON.toJson(vStatus), Doors.class);
- if (doorState != null) {
- assertEquals(VehicleStatusUtils.checkClosed(doorState), st.toString(), "Doors Closed");
- } else {
- assertTrue(false);
- }
-
- break;
- case WINDOWS:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- Windows windowState = GSON.fromJson(GSON.toJson(vStatus), Windows.class);
- if (windowState != null) {
- if (specialHandlingMap.containsKey(WINDOWS)) {
- assertEquals(specialHandlingMap.get(WINDOWS).toString(), st.toString(), "Windows");
- } else {
- assertEquals(VehicleStatusUtils.checkClosed(windowState), st.toString(), "Windows");
- }
- } else {
- assertTrue(false);
- }
-
- break;
- case CHECK_CONTROL:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- if (specialHandlingMap.containsKey(CHECK_CONTROL)) {
- assertEquals(specialHandlingMap.get(CHECK_CONTROL).toString(), st.toString(), "Check Control");
- } else {
- assertEquals(Converter.toTitleCase(VehicleStatusUtils.checkControlActive(vStatus)), st.toString(),
- "Check Control");
- }
- break;
- case CHARGE_STATUS:
- assertTrue(isElectric, "Is Electric");
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- if (vStatus.chargingStatus.contentEquals(Constants.INVALID)) {
- assertEquals(Converter.toTitleCase(vStatus.lastChargingEndReason), st.toString(), "Charge Status");
- } else {
- assertEquals(Converter.toTitleCase(vStatus.chargingStatus), st.toString(), "Charge Status");
- }
- break;
- case CHARGE_REMAINING:
- assertTrue(isElectric, "Is Electric");
- if (vStatus.chargingTimeRemaining == null) {
- assertTrue(state instanceof UnDefType, "expected UndefType");
- } else {
- assertTrue(state instanceof QuantityType);
- qtt = ((QuantityType) state);
- assertEquals(qtt.doubleValue(), vStatus.chargingTimeRemaining);
- assertEquals(Units.MINUTE, qtt.getUnit(), "Minutes");
- }
- break;
- case PLUG_CONNECTION:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.connectionStatus));
- assertEquals(wanted.toString(), st.toString(), "Plug Connection State");
- break;
- case LAST_UPDATE:
- assertTrue(state instanceof DateTimeType);
- dtt = (DateTimeType) state;
- DateTimeType expected = DateTimeType
- .valueOf(Converter.getLocalDateTime(VehicleStatusUtils.getUpdateTime(vStatus)));
- assertEquals(expected.toString(), dtt.toString(), "Last Update");
- break;
- case LAST_UPDATE_REASON:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.updateReason));
- assertEquals(wanted.toString(), st.toString(), "Last Update");
- break;
- case GPS:
- assertTrue(state instanceof PointType);
- pt = (PointType) state;
- assertNotNull(vStatus.position);
- assertEquals(vStatus.position.getCoordinates(), pt.toString(), "Coordinates");
- break;
- case HEADING:
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.DEGREE_ANGLE, qt.getUnit(), "Angle Unit");
- assertNotNull(vStatus.position);
- assertEquals(vStatus.position.heading, qt.intValue(), 0.01, "Heading");
- break;
- case RANGE_RADIUS_ELECTRIC:
- assertTrue(state instanceof QuantityType);
- assertTrue(isElectric);
- qt = (QuantityType) state;
- if (imperial) {
- assertEquals(Converter.guessRangeRadius(vStatus.remainingRangeElectricMls), qt.floatValue(), 1,
- "Range Radius Electric mi");
- } else {
- assertEquals(Converter.guessRangeRadius(vStatus.remainingRangeElectric), qt.floatValue(), 0.1,
- "Range Radius Electric km");
- }
- break;
- case RANGE_RADIUS_ELECTRIC_MAX:
- assertTrue(state instanceof QuantityType);
- assertTrue(isElectric);
- qt = (QuantityType) state;
- if (imperial) {
- assertEquals(Converter.guessRangeRadius(vStatus.maxRangeElectricMls), qt.floatValue(), 1,
- "Range Radius Electric mi");
- } else {
- assertEquals(Converter.guessRangeRadius(vStatus.maxRangeElectric), qt.floatValue(), 0.1,
- "Range Radius Electric km");
- }
- break;
- case RANGE_RADIUS_FUEL:
- assertTrue(state instanceof QuantityType);
- assertTrue(hasFuel);
- qt = (QuantityType) state;
- if (imperial) {
- assertEquals(Converter.guessRangeRadius(vStatus.remainingRangeFuelMls), qt.floatValue(), 1,
- "Range Radius Fuel mi");
- } else {
- assertEquals(Converter.guessRangeRadius(vStatus.remainingRangeFuel), qt.floatValue(), 0.1,
- "Range Radius Fuel km");
- }
- break;
- case RANGE_RADIUS_HYBRID:
- assertTrue(state instanceof QuantityType);
- assertTrue(isHybrid);
- qt = (QuantityType) state;
- if (imperial) {
- assertEquals(
- Converter.guessRangeRadius(
- vStatus.remainingRangeElectricMls + vStatus.remainingRangeFuelMls),
- qt.floatValue(), ALLOWED_MILE_CONVERSION_DEVIATION, "Range Radius Hybrid mi");
- } else {
- assertEquals(
- Converter.guessRangeRadius(vStatus.remainingRangeElectric + vStatus.remainingRangeFuel),
- qt.floatValue(), ALLOWED_KM_ROUND_DEVIATION, "Range Radius Hybrid km");
- }
- break;
- case RANGE_RADIUS_HYBRID_MAX:
- assertTrue(state instanceof QuantityType);
- assertTrue(isHybrid);
- qt = (QuantityType) state;
- if (imperial) {
- assertEquals(
- Converter.guessRangeRadius(vStatus.maxRangeElectricMls + vStatus.remainingRangeFuelMls),
- qt.floatValue(), ALLOWED_MILE_CONVERSION_DEVIATION, "Range Radius Hybrid Max mi");
- } else {
- assertEquals(Converter.guessRangeRadius(vStatus.maxRangeElectric + vStatus.remainingRangeFuel),
- qt.floatValue(), ALLOWED_KM_ROUND_DEVIATION, "Range Radius Hybrid Max km");
- }
- break;
- case DOOR_DRIVER_FRONT:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.doorDriverFront));
- assertEquals(wanted.toString(), st.toString(), "Door");
- break;
- case DOOR_DRIVER_REAR:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.doorDriverRear));
- assertEquals(wanted.toString(), st.toString(), "Door");
- break;
- case DOOR_PASSENGER_FRONT:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.doorPassengerFront));
- assertEquals(wanted.toString(), st.toString(), "Door");
- break;
- case DOOR_PASSENGER_REAR:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.doorPassengerRear));
- assertEquals(wanted.toString(), st.toString(), "Door");
- break;
- case TRUNK:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.trunk));
- assertEquals(wanted.toString(), st.toString(), "Door");
- break;
- case HOOD:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.hood));
- assertEquals(wanted.toString(), st.toString(), "Door");
- break;
- case WINDOW_DOOR_DRIVER_FRONT:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.windowDriverFront));
- assertEquals(wanted.toString(), st.toString(), "Window");
- break;
- case WINDOW_DOOR_DRIVER_REAR:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.windowDriverRear));
- assertEquals(wanted.toString(), st.toString(), "Window");
- break;
- case WINDOW_DOOR_PASSENGER_FRONT:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.windowPassengerFront));
- assertEquals(wanted.toString(), st.toString(), "Window");
- break;
- case WINDOW_DOOR_PASSENGER_REAR:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.windowPassengerRear));
- assertEquals(wanted.toString(), st.toString(), "Window");
- break;
- case WINDOW_REAR:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.rearWindow));
- assertEquals(wanted.toString(), st.toString(), "Window");
- break;
- case SUNROOF:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.sunroof));
- assertEquals(wanted.toString(), st.toString(), "Window");
- break;
- case SERVICE_DATE:
- assertTrue(state instanceof DateTimeType);
- dtt = (DateTimeType) state;
- if (gUid.contentEquals(CHANNEL_GROUP_STATUS)) {
- if (specialHandlingMap.containsKey(SERVICE_DATE)) {
- assertEquals(specialHandlingMap.get(SERVICE_DATE).toString(), dtt.toString(), "Next Service");
- } else {
- String dueDateString = VehicleStatusUtils.getNextServiceDate(vStatus);
- DateTimeType expectedDTT = DateTimeType.valueOf(Converter.getLocalDateTime(dueDateString));
- assertEquals(expectedDTT.toString(), dtt.toString(), "Next Service");
- }
- } else if (gUid.equals(CHANNEL_GROUP_SERVICE)) {
- String dueDateString = vStatus.cbsData.get(0).getDueDate();
- DateTimeType expectedDTT = DateTimeType.valueOf(Converter.getLocalDateTime(dueDateString));
- assertEquals(expectedDTT.toString(), dtt.toString(), "First Service Date");
- }
- break;
- case SERVICE_MILEAGE:
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (gUid.contentEquals(CHANNEL_GROUP_STATUS)) {
- if (imperial) {
- assertEquals(ImperialUnits.MILE, qt.getUnit(), "Next Service Miles");
- assertEquals(VehicleStatusUtils.getNextServiceMileage(vStatus), qt.intValue(), "Mileage");
- } else {
- assertEquals(KILOMETRE, qt.getUnit(), "Next Service KM");
- assertEquals(VehicleStatusUtils.getNextServiceMileage(vStatus), qt.intValue(), "Mileage");
- }
- } else if (gUid.equals(CHANNEL_GROUP_SERVICE)) {
- if (imperial) {
- assertEquals(ImperialUnits.MILE, qt.getUnit(), "First Service Miles");
- assertEquals(vStatus.cbsData.get(0).cbsRemainingMileage, qt.intValue(),
- "First Service Mileage");
- } else {
- assertEquals(KILOMETRE, qt.getUnit(), "First Service KM");
- assertEquals(vStatus.cbsData.get(0).cbsRemainingMileage, qt.intValue(),
- "First Service Mileage");
- }
- }
- break;
- case NAME:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- switch (gUid) {
- case CHANNEL_GROUP_SERVICE:
- wanted = StringType.valueOf(Converter.toTitleCase(Constants.NO_ENTRIES));
- if (!vStatus.cbsData.isEmpty()) {
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.cbsData.get(0).getType()));
- }
- assertEquals(wanted.toString(), st.toString(), "Service Name");
- break;
- case CHANNEL_GROUP_CHECK_CONTROL:
- wanted = StringType.valueOf(Constants.NO_ENTRIES);
- if (!vStatus.checkControlMessages.isEmpty()) {
- wanted = StringType.valueOf(vStatus.checkControlMessages.get(0).ccmDescriptionShort);
- }
- assertEquals(wanted.toString(), st.toString(), "CheckControl Name");
- break;
- default:
- assertFalse(true, "Channel " + channelUID + " " + state + " not found");
- break;
- }
- break;
- case DETAILS:
- assertTrue(state instanceof StringType);
- st = (StringType) state;
- switch (gUid) {
- case CHANNEL_GROUP_SERVICE:
- wanted = StringType.valueOf(Converter.toTitleCase(Constants.NO_ENTRIES));
- if (!vStatus.cbsData.isEmpty()) {
- wanted = StringType.valueOf(Converter.toTitleCase(vStatus.cbsData.get(0).getDescription()));
- }
- assertEquals(wanted.toString(), st.toString(), "Service Details");
- break;
- case CHANNEL_GROUP_CHECK_CONTROL:
- wanted = StringType.valueOf(Constants.NO_ENTRIES);
- if (!vStatus.checkControlMessages.isEmpty()) {
- wanted = StringType.valueOf(vStatus.checkControlMessages.get(0).ccmDescriptionLong);
- }
- assertEquals(wanted.toString(), st.toString(), "CheckControl Details");
- break;
- default:
- assertFalse(true, "Channel " + channelUID + " " + state + " not found");
- break;
- }
- break;
- case DATE:
- assertTrue(state instanceof DateTimeType);
- dtt = (DateTimeType) state;
- switch (gUid) {
- case CHANNEL_GROUP_SERVICE:
- String dueDateString = Constants.NULL_DATE;
- if (!vStatus.cbsData.isEmpty()) {
- dueDateString = vStatus.cbsData.get(0).getDueDate();
- }
- DateTimeType expectedDTT = DateTimeType.valueOf(Converter.getLocalDateTime(dueDateString));
- assertEquals(expectedDTT.toString(), dtt.toString(), "ServiceSate");
- break;
- default:
- assertFalse(true, "Channel " + channelUID + " " + state + " not found");
- break;
- }
- break;
- default:
- // fail in case of unknown update
- assertFalse(true, "Channel " + channelUID + " " + state + " not found");
- break;
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.*;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.measure.Unit;
-import javax.measure.quantity.Length;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.VehicleType;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.LastTrip;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.statistics.LastTripContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.core.library.types.DateTimeType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.ImperialUnits;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.types.State;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link TripWrapper} Test json responses from ConnectedDrive Portal
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class TripWrapper {
- private static final Gson GSON = new Gson();
- private static final Unit<Length> MILES = ImperialUnits.MILE;
-
- private LastTrip lastTrip;
- private boolean imperial;
- private boolean isElectric;
- private boolean hasFuel;
- private boolean isHybrid;
-
- private Map<String, State> specialHandlingMap = new HashMap<String, State>();
-
- public TripWrapper(String type, boolean imperial, String statusJson) {
- this.imperial = imperial;
- hasFuel = type.equals(VehicleType.CONVENTIONAL.toString()) || type.equals(VehicleType.PLUGIN_HYBRID.toString())
- || type.equals(VehicleType.ELECTRIC_REX.toString());
- isElectric = type.equals(VehicleType.PLUGIN_HYBRID.toString())
- || type.equals(VehicleType.ELECTRIC_REX.toString()) || type.equals(VehicleType.ELECTRIC.toString());
- isHybrid = hasFuel && isElectric;
- LastTripContainer container = GSON.fromJson(statusJson, LastTripContainer.class);
- assertNotNull(container);
- assertNotNull(container.lastTrip);
- lastTrip = container.lastTrip;
- }
-
- /**
- * Test results auctomatically against json values
- *
- * @param channels
- * @param states
- * @return
- */
- public boolean checkResults(@Nullable List<ChannelUID> channels, @Nullable List<State> states) {
- assertNotNull(channels);
- assertNotNull(states);
- assertTrue(channels.size() == states.size(), "Same list sizes");
- for (int i = 0; i < channels.size(); i++) {
- checkResult(channels.get(i), states.get(i));
- }
- return true;
- }
-
- /**
- * Add a specific check for a value e.g. hard coded "Upcoming Service" in order to check the right ordering
- *
- * @param specialHand
- * @return
- */
- public TripWrapper append(Map<String, State> compareMap) {
- specialHandlingMap.putAll(compareMap);
- return this;
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private void checkResult(ChannelUID channelUID, State state) {
- String cUid = channelUID.getIdWithoutGroup();
- QuantityType<Length> qt;
- DateTimeType dtt;
- switch (cUid) {
- case DATE:
- assertTrue(state instanceof DateTimeType);
- dtt = ((DateTimeType) state);
- DateTimeType expected = DateTimeType.valueOf(Converter.getLocalDateTimeWithoutOffest(lastTrip.date));
- assertEquals(expected.toString(), dtt.toString(), "Trip Date");
- break;
- case DURATION:
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.MINUTE, qt.getUnit(), "Minute");
- assertEquals(lastTrip.duration, qt.floatValue(), 0.1, "Duration");
- break;
- case DISTANCE:
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- if (imperial) {
- assertEquals(MILES, qt.getUnit(), "Miles");
- assertEquals(lastTrip.totalDistance / Converter.MILES_TO_KM_RATIO, qt.floatValue(), 0.1,
- "Distance");
-
- } else {
- assertEquals(Constants.KILOMETRE_UNIT, qt.getUnit(), "KM");
- assertEquals(lastTrip.totalDistance, qt.floatValue(), 0.1, "Distance");
- }
- break;
- case AVG_CONSUMPTION:
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.KILOWATT_HOUR, qt.getUnit(), "kw/h");
- if (imperial) {
- assertEquals(lastTrip.avgElectricConsumption * Converter.MILES_TO_KM_RATIO, qt.floatValue(), 0.1,
- "Avg Consumption");
- } else {
- assertEquals(lastTrip.avgElectricConsumption, qt.floatValue(), 0.1, "Avg Consumption");
- }
- break;
- case AVG_COMBINED_CONSUMPTION:
- assertTrue(isHybrid, "Is Hybrid");
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.LITRE, qt.getUnit(), "Liter");
- if (imperial) {
- assertEquals(Converter.round(lastTrip.avgCombinedConsumption * Converter.MILES_TO_KM_RATIO),
- qt.floatValue(), 0.01, "Percent");
- } else {
- assertEquals(Converter.round(lastTrip.avgCombinedConsumption), qt.floatValue(), 0.01, "Percent");
- }
- break;
- case AVG_RECUPERATION:
- assertTrue(state instanceof QuantityType);
- qt = ((QuantityType) state);
- assertEquals(Units.KILOWATT_HOUR, qt.getUnit(), "kw/h");
- if (imperial) {
- assertEquals(lastTrip.avgRecuperation * Converter.MILES_TO_KM_RATIO, qt.floatValue(), 0.1,
- "Avg Recuperation");
- } else {
- assertEquals(lastTrip.avgRecuperation, qt.floatValue(), 0.1, "Avg Recuperation");
- }
- break;
- default:
- // fail in case of unknown update
- assertFalse(true, "Channel " + channelUID + " " + state + " not found");
- break;
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.dto;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.compat.VehicleAttributesContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.CBSMessage;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.Position;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatus;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatusContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link VehicleStatusTest} Test json responses from ConnectedDrive Portal
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class VehicleStatusTest {
- private static final Gson GSON = new Gson();
-
- @Test
- public void testBevRexValues() {
- String resource1 = FileReader.readFileInString("src/test/resources/webapi/vehicle-status.json");
- VehicleStatusContainer status = GSON.fromJson(resource1, VehicleStatusContainer.class);
- VehicleStatus vStatus = status.vehicleStatus;
- assertEquals(17273.0, vStatus.mileage, 0.1, "Mileage");
- Position p = vStatus.position;
- assertEquals(219, p.heading, "Heading");
-
- assertEquals("NA", vStatus.dcsCchActivation, "DCS Activation");
- assertEquals(false, vStatus.dcsCchOngoing, "DCS Ongoing");
- }
-
- @Test
- public void testServices() {
- String resource1 = FileReader.readFileInString("src/test/resources/webapi/vehicle-status.json");
- VehicleStatusContainer status = GSON.fromJson(resource1, VehicleStatusContainer.class);
- VehicleStatus vStatus = status.vehicleStatus;
- List<CBSMessage> services = vStatus.cbsData;
- CBSMessage message = services.get(0);
- assertEquals(15345, message.cbsRemainingMileage, "Service Mileage");
- message = services.get(1);
- assertEquals(-1, message.cbsRemainingMileage, "Service Mileage ");
- }
-
- @Test
- public void testCompatibility() {
- String resource = FileReader.readFileInString("src/test/resources/api/vehicle/vehicle-ccm.json");
- VehicleAttributesContainer vac = GSON.fromJson(resource, VehicleAttributesContainer.class);
- assertEquals("Laden nicht möglich", vac.vehicleMessages.ccmMessages.get(0).text, "CCM");
- // Time Test to be removed - different Machines = different Time Zones
- // VehicleStatusContainer vsc = GSON.fromJson(vac.transform(), VehicleStatusContainer.class);
- // assertEquals("27.09.2020 13:18", vsc.vehicleStatus.getUpdateTime(), "UTC DateTime");
- // String ldt = Converter.getLocalDateTime(vsc.vehicleStatus.getUpdateTime());
- // assertEquals("2020-09-27T15:18:00", ldt.toString(), "Local DateTime");
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.VehicleType;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.LifetimeWrapper;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandlerCallback;
-import org.openhab.core.types.State;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link AllTripTests} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class AllTripTests {
- private final Logger logger = LoggerFactory.getLogger(VehicleHandler.class);
-
- private static final int HYBRID_CALL_TIMES = 6;
-
- @Nullable
- ArgumentCaptor<ChannelUID> channelCaptor;
- @Nullable
- ArgumentCaptor<State> stateCaptor;
- @Nullable
- ThingHandlerCallback tc;
- @Nullable
- VehicleHandler cch;
- @Nullable
- List<ChannelUID> allChannels;
- @Nullable
- List<State> allStates;
- String driveTrain = Constants.EMPTY;
- boolean imperial;
-
- /**
- * Prepare environment for Vehicle Status Updates
- */
- public void setup(String type, boolean imperial) {
- driveTrain = type;
- this.imperial = imperial;
- Thing thing = mock(Thing.class);
- when(thing.getUID()).thenReturn(new ThingUID("testbinding", "test"));
- BMWConnectedDriveOptionProvider op = mock(BMWConnectedDriveOptionProvider.class);
- cch = new VehicleHandler(thing, op, type, imperial);
- tc = mock(ThingHandlerCallback.class);
- cch.setCallback(tc);
- channelCaptor = ArgumentCaptor.forClass(ChannelUID.class);
- stateCaptor = ArgumentCaptor.forClass(State.class);
- }
-
- private boolean testTrip(String statusContent, int callbacksExpected, Optional<Map<String, State>> concreteChecks) {
- assertNotNull(statusContent);
- cch.allTripsCallback.onResponse(statusContent);
- verify(tc, times(callbacksExpected)).stateUpdated(channelCaptor.capture(), stateCaptor.capture());
- allChannels = channelCaptor.getAllValues();
- allStates = stateCaptor.getAllValues();
-
- assertNotNull(driveTrain);
- LifetimeWrapper checker = new LifetimeWrapper(driveTrain, imperial, statusContent);
- trace();
- if (concreteChecks.isPresent()) {
- return checker.append(concreteChecks.get()).checkResults(allChannels, allStates);
- } else {
- return checker.checkResults(allChannels, allStates);
- }
- }
-
- private void trace() {
- for (int i = 0; i < allChannels.size(); i++) {
- logger.info("Channel {} {}", allChannels.get(i), allStates.get(i));
- }
- }
-
- @Test
- public void testi3Rex() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/I01_REX/all-trips.json");
- assertTrue(testTrip(content, HYBRID_CALL_TIMES, Optional.empty()));
- }
-
- @Test
- public void test530E() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.PLUGIN_HYBRID.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/530E/all-trips.json");
- assertTrue(testTrip(content, HYBRID_CALL_TIMES, Optional.empty()));
- }
-
- @Test
- public void testi3RexImperial() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/I01_REX/all-trips.json");
- assertTrue(testTrip(content, HYBRID_CALL_TIMES, Optional.empty()));
- }
-
- @Test
- public void test530EImperial() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.PLUGIN_HYBRID.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/530E/all-trips.json");
- assertTrue(testTrip(content, HYBRID_CALL_TIMES, Optional.empty()));
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.mockito.Mockito.*;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.VehicleType;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandlerCallback;
-import org.openhab.core.types.State;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link ChargeProfileTest} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class ChargeProfileTest {
- private final Logger logger = LoggerFactory.getLogger(VehicleHandler.class);
-
- private static final int PROFILE_CALLBACK_NUMBER = 37;
-
- @Nullable
- ArgumentCaptor<ChannelUID> channelCaptor;
- @Nullable
- ArgumentCaptor<State> stateCaptor;
- @Nullable
- ThingHandlerCallback tc;
- @Nullable
- VehicleHandler cch;
- @Nullable
- List<ChannelUID> allChannels;
- @Nullable
- List<State> allStates;
- String driveTrain = Constants.EMPTY;
- boolean imperial;
-
- /**
- * Prepare environment for Vehicle Status Updates
- */
- public void setup(String type, boolean imperial) {
- driveTrain = type;
- this.imperial = imperial;
- Thing thing = mock(Thing.class);
- when(thing.getUID()).thenReturn(new ThingUID("testbinding", "test"));
- BMWConnectedDriveOptionProvider op = mock(BMWConnectedDriveOptionProvider.class);
- cch = new VehicleHandler(thing, op, type, imperial);
- tc = mock(ThingHandlerCallback.class);
- cch.setCallback(tc);
- channelCaptor = ArgumentCaptor.forClass(ChannelUID.class);
- stateCaptor = ArgumentCaptor.forClass(State.class);
- }
-
- private boolean testProfile(String statusContent, int callbacksExpected) {
- assertNotNull(statusContent);
-
- cch.chargeProfileCallback.onResponse(statusContent);
- verify(tc, times(callbacksExpected)).stateUpdated(channelCaptor.capture(), stateCaptor.capture());
- allChannels = channelCaptor.getAllValues();
- allStates = stateCaptor.getAllValues();
-
- assertNotNull(driveTrain);
- trace();
- return true;
- }
-
- private void trace() {
- for (int i = 0; i < allChannels.size(); i++) {
- logger.info("Channel {} {}", allChannels.get(i), allStates.get(i));
- }
- }
-
- /**
- * Channel testbinding::test:charge#profile-climate ON
- * Channel testbinding::test:charge#profile-mode IMMEDIATE_CHARGING
- * Channel testbinding::test:charge#window-start 11:00
- * Channel testbinding::test:charge#window-end 17:00
- * Channel testbinding::test:charge#timer1-departure 05:00
- * Channel testbinding::test:charge#timer1-enabled OFF
- * Channel testbinding::test:charge#timer1-days MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY
- * Channel testbinding::test:charge#timer2-departure 12:00
- * Channel testbinding::test:charge#timer2-enabled ON
- * Channel testbinding::test:charge#timer2-days SATURDAY
- * Channel testbinding::test:charge#timer3-departure 00:00
- * Channel testbinding::test:charge#timer3-enabled OFF
- * Channel testbinding::test:charge#timer3-days
- */
- @Test
- public void testChargingProfile() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/webapi/charging-profile.json");
- testProfile(content, PROFILE_CALLBACK_NUMBER);
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConfiguration;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.BimmerConstants;
-
-/**
- * The {@link ConfigurationTest} test different configurations
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class ConfigurationTest {
-
- @Test
- public void testAuthServerMap() {
- ConnectedDriveConfiguration cdc = new ConnectedDriveConfiguration();
- assertFalse(ConnectedDriveBridgeHandler.checkConfiguration(cdc));
- cdc.userName = "a";
- assertFalse(ConnectedDriveBridgeHandler.checkConfiguration(cdc));
- cdc.password = "b";
- assertFalse(ConnectedDriveBridgeHandler.checkConfiguration(cdc));
- cdc.region = "c";
- assertFalse(ConnectedDriveBridgeHandler.checkConfiguration(cdc));
- cdc.region = BimmerConstants.REGION_NORTH_AMERICA;
- assertTrue(ConnectedDriveBridgeHandler.checkConfiguration(cdc));
- cdc.region = BimmerConstants.REGION_ROW;
- assertTrue(ConnectedDriveBridgeHandler.checkConfiguration(cdc));
- cdc.region = BimmerConstants.REGION_CHINA;
- assertTrue(ConnectedDriveBridgeHandler.checkConfiguration(cdc));
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.mockito.Mockito.*;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandlerCallback;
-import org.openhab.core.types.State;
-
-/**
- * The {@link ErrorResponseTest} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class ErrorResponseTest {
- @Nullable
- ArgumentCaptor<ChannelUID> channelCaptor;
- @Nullable
- ArgumentCaptor<State> stateCaptor;
- @Nullable
- ThingHandlerCallback tc;
- @Nullable
- VehicleHandler cch;
- @Nullable
- List<ChannelUID> allChannels;
- @Nullable
- List<State> allStates;
- @Nullable
- String driveTrain;
- boolean imperial;
-
- /**
- * Prepare environment for Vehicle Status Updates
- */
- public void setup(String type, boolean imperial) {
- driveTrain = type;
- this.imperial = imperial;
- Thing thing = mock(Thing.class);
- when(thing.getUID()).thenReturn(new ThingUID("testbinding", "test"));
- BMWConnectedDriveOptionProvider op = mock(BMWConnectedDriveOptionProvider.class);
- cch = new VehicleHandler(thing, op, type, imperial);
- tc = mock(ThingHandlerCallback.class);
- cch.setCallback(tc);
- channelCaptor = ArgumentCaptor.forClass(ChannelUID.class);
- stateCaptor = ArgumentCaptor.forClass(State.class);
- }
-
- @Test
- public void testErrorResponseCallbacks() {
- String error = "{\"error\":true,\"reason\":\"offline\"}";
- setup("BEV", false);
- cch.vehicleStatusCallback.onResponse(error);
- cch.allTripsCallback.onResponse(error);
- cch.lastTripCallback.onResponse(error);
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.NetworkError;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.core.io.net.http.HttpClientFactory;
-import org.openhab.core.thing.Bridge;
-import org.openhab.core.thing.ThingUID;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link FingerprintTest} Test Discovery Results
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class FingerprintTest {
- private final Logger logger = LoggerFactory.getLogger(FingerprintTest.class);
-
- public void testDiscoveryFingerprint() {
- Bridge b = mock(Bridge.class);
- when(b.getUID()).thenReturn(new ThingUID("bmwconnecteddrive", "account", "user"));
- HttpClientFactory hcf = mock(HttpClientFactory.class);
- ConnectedDriveBridgeHandler bh = new ConnectedDriveBridgeHandler(b, hcf);
- // when(bh.getThing()).thenReturn(b);
-
- bh.onResponse(Constants.EMPTY_JSON);
- assertEquals(Constants.EMPTY_JSON, bh.getDiscoveryFingerprint(), "Empty Response");
-
- bh.onResponse(null);
- assertEquals(Constants.EMPTY_JSON, bh.getDiscoveryFingerprint(), "Empty Response");
-
- String content = FileReader.readFileInString("src/test/resources/webapi/connected-drive-account-info.json");
- bh.onResponse(content);
- String fingerprint = bh.getDiscoveryFingerprint();
- logger.info("{}", fingerprint);
- assertFalse(fingerprint.contains("My Real"), "Anonymous Fingerprint");
- assertFalse(fingerprint.contains("MY_REAL_VIN"), "Anonymous Fingerprint");
-
- NetworkError err = new NetworkError();
- err.url = "Some URL";
- err.status = 500;
- err.reason = "Internal Server Error";
- bh.onError(err);
- assertEquals(err.toJson(), bh.getDiscoveryFingerprint(), "Empty Response");
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.VehicleType;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.TripWrapper;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandlerCallback;
-import org.openhab.core.types.State;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link LastTripTests} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class LastTripTests {
- private final Logger logger = LoggerFactory.getLogger(VehicleHandler.class);
-
- private static final int HYBRID_CALL_TIMES = 6;
-
- @Nullable
- ArgumentCaptor<ChannelUID> channelCaptor;
- @Nullable
- ArgumentCaptor<State> stateCaptor;
- @Nullable
- ThingHandlerCallback tc;
- @Nullable
- VehicleHandler cch;
- @Nullable
- List<ChannelUID> allChannels;
- @Nullable
- List<State> allStates;
- String driveTrain = Constants.EMPTY;
- boolean imperial;
-
- /**
- * Prepare environment for Vehicle Status Updates
- */
- public void setup(String type, boolean imperial) {
- driveTrain = type;
- this.imperial = imperial;
- Thing thing = mock(Thing.class);
- when(thing.getUID()).thenReturn(new ThingUID("testbinding", "test"));
- BMWConnectedDriveOptionProvider op = mock(BMWConnectedDriveOptionProvider.class);
- cch = new VehicleHandler(thing, op, type, imperial);
- tc = mock(ThingHandlerCallback.class);
- cch.setCallback(tc);
- channelCaptor = ArgumentCaptor.forClass(ChannelUID.class);
- stateCaptor = ArgumentCaptor.forClass(State.class);
- }
-
- private boolean testTrip(String statusContent, int callbacksExpected, Optional<Map<String, State>> concreteChecks) {
- assertNotNull(statusContent);
- cch.lastTripCallback.onResponse(statusContent);
- verify(tc, times(callbacksExpected)).stateUpdated(channelCaptor.capture(), stateCaptor.capture());
- allChannels = channelCaptor.getAllValues();
- allStates = stateCaptor.getAllValues();
-
- assertNotNull(driveTrain);
- TripWrapper checker = new TripWrapper(driveTrain, imperial, statusContent);
- trace();
- if (concreteChecks.isPresent()) {
- return checker.append(concreteChecks.get()).checkResults(allChannels, allStates);
- } else {
- return checker.checkResults(allChannels, allStates);
- }
- }
-
- private void trace() {
- for (int i = 0; i < allChannels.size(); i++) {
- logger.info("Channel {} {}", allChannels.get(i), allStates.get(i));
- }
- }
-
- @Test
- public void testi3Rex() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/I01_REX/last-trip.json");
- assertTrue(testTrip(content, HYBRID_CALL_TIMES, Optional.empty()));
- }
-
- @Test
- public void test530E() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.PLUGIN_HYBRID.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/530E/last-trip.json");
- assertTrue(testTrip(content, HYBRID_CALL_TIMES, Optional.empty()));
- }
-
- @Test
- public void testi3RexImperial() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/I01_REX/last-trip.json");
- assertTrue(testTrip(content, HYBRID_CALL_TIMES, Optional.empty()));
- }
-
- @Test
- public void test530EImperial() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.PLUGIN_HYBRID.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/530E/last-trip.json");
- assertTrue(testTrip(content, HYBRID_CALL_TIMES, Optional.empty()));
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.bmwconnecteddrive.internal.handler.simulation.Injector;
-
-/**
- * The {@link SimulationTest} Assures simulation is off
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class SimulationTest {
-
- @Test
- public void testSimulationOff() {
- assertFalse(Injector.isActive(), "Simulation off");
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.handler;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.VehicleType;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.StatusWrapper;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.compat.VehicleAttributesContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.core.library.types.StringType;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandlerCallback;
-import org.openhab.core.types.State;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link VehicleTests} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class VehicleTests {
- private final Logger logger = LoggerFactory.getLogger(VehicleHandler.class);
-
- private static final int STATUS_ELECTRIC = 12;
- private static final int STATUS_CONV = 8;
- private static final int RANGE_HYBRID = 12;
- private static final int RANGE_CONV = 4;
- private static final int RANGE_ELECTRIC = 5;
- private static final int DOORS = 12;
- private static final int CHECK_EMPTY = 3;
- private static final int CHECK_AVAILABLE = 3;
- private static final int SERVICE_AVAILABLE = 4;
- private static final int SERVICE_EMPTY = 4;
- private static final int POSITION = 2;
-
- @Nullable
- ArgumentCaptor<ChannelUID> channelCaptor;
- @Nullable
- ArgumentCaptor<State> stateCaptor;
- @Nullable
- ThingHandlerCallback tc;
- @Nullable
- VehicleHandler cch;
- @Nullable
- List<ChannelUID> allChannels;
- @Nullable
- List<State> allStates;
- String driveTrain = Constants.EMPTY;
- boolean imperial;
-
- /**
- * Prepare environment for Vehicle Status Updates
- */
- public void setup(String type, boolean imperial) {
- driveTrain = type;
- this.imperial = imperial;
- Thing thing = mock(Thing.class);
- when(thing.getUID()).thenReturn(new ThingUID("testbinding", "test"));
- BMWConnectedDriveOptionProvider op = mock(BMWConnectedDriveOptionProvider.class);
- cch = new VehicleHandler(thing, op, type, imperial);
- tc = mock(ThingHandlerCallback.class);
- cch.setCallback(tc);
- channelCaptor = ArgumentCaptor.forClass(ChannelUID.class);
- stateCaptor = ArgumentCaptor.forClass(State.class);
- }
-
- private boolean testVehicle(String statusContent, int callbacksExpected,
- Optional<Map<String, State>> concreteChecks) {
- assertNotNull(statusContent);
- cch.vehicleStatusCallback.onResponse(statusContent);
- verify(tc, times(callbacksExpected)).stateUpdated(channelCaptor.capture(), stateCaptor.capture());
- allChannels = channelCaptor.getAllValues();
- allStates = stateCaptor.getAllValues();
-
- assertNotNull(driveTrain);
- StatusWrapper checker = new StatusWrapper(driveTrain, imperial, statusContent);
- trace();
- if (concreteChecks.isPresent()) {
- return checker.append(concreteChecks.get()).checkResults(allChannels, allStates);
- } else {
- return checker.checkResults(allChannels, allStates);
- }
- }
-
- private void trace() {
- for (int i = 0; i < allChannels.size(); i++) {
- logger.info("Channel {} {}", allChannels.get(i), allStates.get(i));
- }
- }
-
- /**
- * Test various Vehicles from users which delivered their fingerprint.
- * The tests are checking the chain from "JSON to Channel update".
- * Checks are done in an automated way cross checking the data from JSON and data delivered via Channel.
- * Also important the updates are counted in order to check if code changes are affecting Channel Updates.
- *
- * With the given output the updated Channels are visible.
- * Example:
- *
- * testi3Rex
- * [main] INFO org.eclipse.jetty.util.log - Logging initialized @1731ms
- * Channel testbinding::test:status#lock Secured
- * Channel testbinding::test:status#service-date 2021-11-01T13:00:00.000+0100
- * Channel testbinding::test:status#service-mileage -1.0 km
- * Channel testbinding::test:status#check-control Not Active
- * Channel testbinding::test:status#last-update 2020-08-24T17:55:32.000+0200
- * Channel testbinding::test:status#doors CLOSED
- * Channel testbinding::test:status#windows CLOSED
- * Channel testbinding::test:doors#driver-front CLOSED
- * Channel testbinding::test:doors#driver-rear CLOSED
- * Channel testbinding::test:doors#passenger-front CLOSED
- * Channel testbinding::test:doors#passenger-rear CLOSED
- * Channel testbinding::test:doors#trunk CLOSED
- * Channel testbinding::test:doors#hood CLOSED
- * Channel testbinding::test:doors#window-driver-front CLOSED
- * Channel testbinding::test:doors#window-driver-rear CLOSED
- * Channel testbinding::test:doors#window-passenger-front CLOSED
- * Channel testbinding::test:doors#window-passenger-rear CLOSED
- * Channel testbinding::test:doors#window-rear INVALID
- * Channel testbinding::test:doors#sunroof CLOSED
- * Channel testbinding::test:range#mileage 17273.0 km
- * Channel testbinding::test:range#electric 148.0 km
- * Channel testbinding::test:range#radius-electric 118.4 km
- * Channel testbinding::test:range#fuel 70.0 km
- * Channel testbinding::test:range#radius-fuel 56.0 km
- * Channel testbinding::test:range#hybrid 218.0 km
- * Channel testbinding::test:range#radius-hybrid 174.4 km
- * Channel testbinding::test:range#soc 71.0 %
- * Channel testbinding::test:range#remaining-fuel 4.0 l
- * Channel testbinding::test:status#charge Charging Goal Reached
- * Channel testbinding::test:check#size 0
- * Channel testbinding::test:check#name INVALID
- * Channel testbinding::test:check#mileage -1.0 km
- * Channel testbinding::test:check#index -1
- * Channel testbinding::test:service#size 4
- * Channel testbinding::test:service#name Brake Fluid
- * Channel testbinding::test:service#date 2021-11-01T13:00:00.000+0100
- * Channel testbinding::test:service#mileage 15345.0 km
- * Channel testbinding::test:service#index 0
- * Channel testbinding::test:location#latitude 50.55604934692383
- * Channel testbinding::test:location#longitude 8.4956693649292
- * Channel testbinding::test:location#heading 219.0 °
- *
- */
-
- @Test
- public void testi3Rex() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/webapi/vehicle-status.json");
- assertTrue(testVehicle(content,
- STATUS_ELECTRIC + RANGE_HYBRID + DOORS + CHECK_EMPTY + SERVICE_AVAILABLE + POSITION, Optional.empty()));
- }
-
- @Test
- public void testi3RexMiles() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/webapi/vehicle-status.json");
- // assertTrue(testVehicle(content, HYBRID_CALL_TIMES + LIST_UPDATES, Optional.empty()));
- assertTrue(testVehicle(content,
- STATUS_ELECTRIC + RANGE_HYBRID + DOORS + CHECK_EMPTY + SERVICE_AVAILABLE + POSITION, Optional.empty()));
- }
-
- @Test
- public void testF15() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/F15/status.json");
- // Check earliest Service by hard
- Map<String, State> m = new HashMap<String, State>();
- // Don>'t test on concrete timestamp - it's is different on each machine
- // Check for cbsType which is "Oil" instead
- // m.put(ConnectedDriveConstants.SERVICE_DATE, DateTimeType.valueOf("2018-06-01T14:00:00.000+0200"));
- m.put(ConnectedDriveConstants.NAME, StringType.valueOf("Oil"));
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_AVAILABLE + CHECK_EMPTY,
- Optional.of(m)));
- }
-
- @Test
- public void testF15Miles() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/F15/status.json");
- // Check earliest Service by hard
- Map<String, State> m = new HashMap<String, State>();
- // Don>'t test on concrete timestamp - it's idfferent on each machine
- // Check for cbsType which is "Oil" instead
- // m.put(ConnectedDriveConstants.SERVICE_DATE, DateTimeType.valueOf("2018-06-01T14:00:00.000+0200"));
- m.put(ConnectedDriveConstants.NAME, StringType.valueOf("Oil"));
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_AVAILABLE + CHECK_EMPTY,
- Optional.of(m)));
- }
-
- @Test
- public void testF31() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/F31/status.json");
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_AVAILABLE + CHECK_EMPTY,
- Optional.empty()));
- }
-
- @Test
- public void testF31Miles() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/F31/status.json");
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_AVAILABLE + CHECK_EMPTY,
- Optional.empty()));
- }
-
- @Test
- public void testF35() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/F35/status.json");
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_EMPTY + CHECK_EMPTY,
- Optional.empty()));
- }
-
- @Test
- public void testF35Miles() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/F35/status.json");
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_EMPTY + CHECK_EMPTY,
- Optional.empty()));
- }
-
- @Test
- public void testF45() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/F45/status.json");
- // assertTrue(testVehicle(content, 27, Optional.empty()));
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_EMPTY + CHECK_EMPTY + POSITION,
- Optional.empty()));
- }
-
- @Test
- public void testF45Miles() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/F45/status.json");
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_EMPTY + CHECK_EMPTY + POSITION,
- Optional.empty()));
- }
-
- @Test
- public void testF48() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/F48/status.json");
- assertTrue(testVehicle(content,
- STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_AVAILABLE + POSITION, Optional.empty()));
- }
-
- @Test
- public void testF48Miles() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/F48/status.json");
- assertTrue(testVehicle(content,
- STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_AVAILABLE + POSITION, Optional.empty()));
- }
-
- @Test
- public void testG31NBTEvo() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/G31_NBTevo/status.json");
- // assertTrue(testVehicle(content, 27, Optional.empty()));
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
- Optional.empty()));
- }
-
- @Test
- public void testG31NBTEvoMiles() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/G31_NBTevo/status.json");
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
- Optional.empty()));
- }
-
- @Test
- public void testI01NoRex() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/I01_NOREX/status.json");
- assertTrue(testVehicle(content,
- STATUS_ELECTRIC + DOORS + RANGE_ELECTRIC + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
- Optional.empty()));
- }
-
- @Test
- public void testI01NoRexMiles() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/I01_NOREX/status.json");
- assertTrue(testVehicle(content,
- STATUS_ELECTRIC + DOORS + RANGE_ELECTRIC + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
- Optional.empty()));
- }
-
- @Test
- public void testI01Rex() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/I01_REX/status.json");
- assertTrue(testVehicle(content,
- STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION, Optional.empty()));
- }
-
- @Test
- public void testI01RexMiles() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/I01_REX/status.json");
- assertTrue(testVehicle(content,
- STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION, Optional.empty()));
- }
-
- @Test
- public void test318iF31() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/F31/status-318i.json");
- Map<String, State> m = new HashMap<String, State>();
- m.put(ConnectedDriveConstants.WINDOWS, StringType.valueOf(Constants.INTERMEDIATE));
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
- Optional.empty()));
- }
-
- @Test
- public void test318iF31Miles() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/F31/status-318i.json");
- Map<String, State> m = new HashMap<String, State>();
- m.put(ConnectedDriveConstants.WINDOWS, StringType.valueOf(Constants.INTERMEDIATE));
- assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
- Optional.empty()));
- }
-
- @Test
- public void testI01RexCompat() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/api/vehicle/vehicle-ccm.json");
- VehicleAttributesContainer vac = Converter.getGson().fromJson(content, VehicleAttributesContainer.class);
- assertTrue(testVehicle(Converter.transformLegacyStatus(vac),
- STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_AVAILABLE + POSITION,
- Optional.empty()));
- }
-
- @Test
- public void testI01RexMilesCompat() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.ELECTRIC_REX.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/api/vehicle/vehicle-ccm.json");
- VehicleAttributesContainer vac = Converter.getGson().fromJson(content, VehicleAttributesContainer.class);
- assertTrue(testVehicle(Converter.transformLegacyStatus(vac),
- STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_AVAILABLE + POSITION,
- Optional.empty()));
- }
-
- @Test
- public void testF11Compat() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), false);
- String content = FileReader.readFileInString("src/test/resources/responses/F11/vehicle-status.json");
- VehicleAttributesContainer vac = Converter.getGson().fromJson(content, VehicleAttributesContainer.class);
- assertTrue(testVehicle(Converter.transformLegacyStatus(vac),
- STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION, Optional.empty()));
- }
-
- @Test
- public void testF11MilesCompat() {
- logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
- setup(VehicleType.CONVENTIONAL.toString(), true);
- String content = FileReader.readFileInString("src/test/resources/responses/F11/vehicle-status.json");
- VehicleAttributesContainer vac = Converter.getGson().fromJson(content, VehicleAttributesContainer.class);
- assertTrue(testVehicle(Converter.transformLegacyStatus(vac),
- STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION, Optional.empty()));
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.util;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
-
-/**
- * The {@link FileReader} Helper Util to read test resource files
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class FileReader {
-
- public static String readFileInString(String filename) {
- try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filename), "UTF-8"));) {
- StringBuilder buf = new StringBuilder();
- String sCurrentLine;
-
- while ((sCurrentLine = br.readLine()) != null) {
- buf.append(sCurrentLine);
- }
- return buf.toString();
- } catch (IOException e) {
- // fail if file cannot be read
- assertTrue(false);
- }
- return Constants.UNDEF;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.bmwconnecteddrive.internal.util;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.Locale;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatus;
-import org.openhab.binding.bmwconnecteddrive.internal.dto.status.VehicleStatusContainer;
-import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
-import org.openhab.core.library.types.DateTimeType;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link LocaleTest} is testing locale settings
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@SuppressWarnings("null")
-public class LocaleTest {
- private static final Gson GSON = new Gson();
-
- @Test
- public void languageTest() {
- assertTrue(ConnectedDriveConstants.IMPERIAL_COUNTRIES.contains(Locale.UK.getCountry()), "United Kingdom");
- assertTrue(ConnectedDriveConstants.IMPERIAL_COUNTRIES.contains(Locale.US.getCountry()), "United States");
- assertFalse(ConnectedDriveConstants.IMPERIAL_COUNTRIES.contains(Locale.FRANCE.getCountry()), "France");
- assertFalse(ConnectedDriveConstants.IMPERIAL_COUNTRIES.contains(Locale.GERMAN.getCountry()), "Germany");
- }
-
- public void testTimeUTCToLocaleTime() {
- String resource1 = FileReader.readFileInString("src/test/resources/webapi/vehicle-status.json");
- VehicleStatusContainer status = GSON.fromJson(resource1, VehicleStatusContainer.class);
- VehicleStatus vStatus = status.vehicleStatus;
-
- String inputTime = vStatus.internalDataTimeUTC;
- String localeTime = Converter.getLocalDateTime(inputTime);
- String dateTimeType = DateTimeType.valueOf(localeTime).toString();
- assertEquals("2020-08-24T15:55:32", inputTime, "Input DateTime");
- assertEquals("2020-08-24T17:55:32", localeTime, "Output DateTime");
- assertEquals("2020-08-24T17:55:32.000+0200", dateTimeType, "DateTimeType Value");
-
- inputTime = vStatus.updateTime;
- localeTime = Converter.getLocalDateTime(inputTime);
- dateTimeType = DateTimeType.valueOf(localeTime).toString();
- assertEquals("2020-08-24T15:55:32+0000", inputTime, "Input DateTime");
- assertEquals("2020-08-24T17:55:32", localeTime, "Output DateTime");
- assertEquals("2020-08-24T17:55:32.000+0200", dateTimeType, "DateTimeType Value");
-
- inputTime = vStatus.updateTime;
- localeTime = Converter.getLocalDateTimeWithoutOffest(inputTime);
- dateTimeType = DateTimeType.valueOf(localeTime).toString();
- assertEquals("2020-08-24T15:55:32+0000", inputTime, "Input DateTime");
- assertEquals("2020-08-24T15:55:32", localeTime, "Output DateTime");
- assertEquals("2020-08-24T15:55:32.000+0200", dateTimeType, "DateTimeType Value");
- }
-
- @Test
- public void testDistance() {
- double lat = 45.678;
- double lon = 8.765;
- double distance = 0.005;
- double dist = Converter.measureDistance(lat, lon, lat + distance, lon + distance);
- assertTrue(dist < 1, "Distance below 1 km");
- }
-}
+++ /dev/null
-{
- "communitySwitch": false,
- "modelType": "I3",
- "scoreList": [
- {
- "attrName": "AVERAGE_ELECTRIC_CONSUMPTION",
- "attrUnit": "KWH_PER_100KM",
- "minValue": 0.0,
- "maxValue": 40.0,
- "lifeTime": 16.5
- },
- {
- "attrName": "AVERAGE_RECUPERATED_ENERGY_PER_100_KM",
- "attrUnit": "KWH_PER_100KM",
- "minValue": 0.0,
- "maxValue": 20.0,
- "lifeTime": 4.5
- },
- {
- "attrName": "CUMULATED_ELECTRIC_DRIVEN_DISTANCE",
- "attrUnit": "KM",
- "minValue": 0.0,
- "maxValue": 16593.4,
- "lifeTime": 16592.4
- },
- {
- "attrName": "LONGEST_DISTANCE_WITHOUT_CHARGING",
- "attrUnit": "KM",
- "minValue": 0.0,
- "maxValue": 270.0,
- "lifeTime": 185.5
- }
- ],
- "lastTripList": [
- {
- "name": "LASTTRIP_DELTA_KM",
- "unit": "KM",
- "lastTrip": "2.0"
- },
- {
- "name": "ACTUAL_DISTANCE_WITHOUT_CHARGING",
- "unit": "KM",
- "lastTrip": "31.0"
- },
- {
- "name": "AVERAGE_ELECTRIC_CONSUMPTION",
- "unit": "KWH_PER_100KM",
- "lastTrip": "14.5"
- },
- {
- "name": "AVERAGE_RECUPERATED_ENERGY_PER_100_KM",
- "unit": "KWH_PER_100KM",
- "lastTrip": "8.0"
- },
- {
- "name": "CUMULATED_ELECTRIC_DRIVEN_DISTANCE",
- "unit": "KM",
- "lastTrip": "16592.4"
- }
- ],
- "lifeTimeList": [],
- "efficiencyQuotient": 44,
- "characteristicList": [
- {
- "characteristic": "TOTAL_CONSUMPTION",
- "quantity": 2
- },
- {
- "characteristic": "AUXILIARY_CONSUMPTION",
- "quantity": 2
- },
- {
- "characteristic": "DRIVING_MODE",
- "quantity": 0
- },
- {
- "characteristic": "ACCELERATION",
- "quantity": 3
- },
- {
- "characteristic": "ANTICIPATION",
- "quantity": 3
- }
- ]
-}
\ No newline at end of file
+++ /dev/null
-{
- "latitude": 56.789,
- "longitude": 8.765,
- "isoCountryCode": "DEU",
- "auxPowerRegular": 1.4,
- "auxPowerEcoPro": 1.2,
- "auxPowerEcoProPlus": 0.4,
- "soc": 25.952999114990234,
- "pendingUpdate": false,
- "vehicleTracking": true,
- "socmax": 29.84
-}
\ No newline at end of file
+++ /dev/null
-{
- "attributesMap": {
- "unitOfLength": "km",
- "sunroof_state": "CLOSED",
- "chargingLogicCurrentlyActive": "NOT_CHARGING",
- "vehicle_tracking": "1",
- "chargeNowAllowed": "NOT_ALLOWED",
- "updateTime_converted": "16.08.2020 15:34",
- "door_driver_rear": "CLOSED",
- "head_unit_pu_software": "07/16",
- "beMaxRangeElectricKm": "203.0",
- "door_passenger_rear": "CLOSED",
- "beRemainingRangeFuelKm": "62.0",
- "Segment_LastTrip_time_segment_end_formatted_date": "15.08.2020",
- "door_driver_front": "CLOSED",
- "shdStatusUnified": "CLOSED",
- "hood_state": "CLOSED",
- "charging_status": "CHARGINGACTIVE",
- "kombi_current_remaining_range_fuel": "62.0",
- "beMaxRangeElectric": "203.0",
- "window_driver_rear": "CLOSED",
- "beRemainingRangeElectricKm": "203.0",
- "mileage": "17044",
- "Segment_LastTrip_time_segment_end_formatted_time": "19:11",
- "beMaxRangeElectricMile": "126.0",
- "Segment_LastTrip_time_segment_end_formatted": "15.08.2020 19:11",
- "lastChargingEndResult": "UNKNOWN",
- "unitOfEnergy": "kWh",
- "beRemainingRangeElectric": "203.0",
- "sunroof_position": "0",
- "soc_hv_percent": "65.4",
- "single_immediate_charging": "isUnused",
- "updateTime_converted_time": "15:34",
- "chargingHVStatus": "FINISHED_FULLY_CHARGED",
- "connectorStatus": "CONNECTED",
- "chargingLevelHv": "100.0",
- "chargingSystemStatus": "CHARGINGACTIVE",
- "fuelPercent": "47",
- "unitOfCombustionConsumption": "l/100km",
- "gps_lat": "xxx",
- "window_driver_front": "CLOSED",
- "Segment_LastTrip_ratio_electric_driven_distance": "100",
- "gps_lng": "xxx",
- "condition_based_services": "00003,OK,2021-11,;00017,OK,2021-11,;00001,OK,2021-11,;00032,OK,2021-11,",
- "window_passenger_front": "CLOSED",
- "window_passenger_rear": "CLOSED",
- "lastChargingEndReason": "UNKNOWN",
- "updateTime_converted_date": "16.08.2020",
- "beRemainingRangeFuelMile": "38.0",
- "beRemainingRangeFuel": "62.0",
- "door_passenger_front": "CLOSED",
- "updateTime_converted_timestamp": "1597592093000",
- "remaining_fuel": "4",
- "charging_inductive_positioning": "not_positioned",
- "heading": "221",
- "lsc_trigger": "DOORSTATECHANGED",
- "lights_parking": "OFF",
- "door_lock_state": "UNLOCKED",
- "updateTime": "16.08.2020 14:34:53 UTC",
- "prognosisWhileChargingStatus": "IS_PERFORMED",
- "head_unit": "EntryNav",
- "trunk_state": "CLOSED",
- "battery_size_max": "33200",
- "charging_connection_type": "CONDUCTIVE",
- "beRemainingRangeElectricMile": "126.0",
- "unitOfElectricConsumption": "kWh/100km",
- "Segment_LastTrip_time_segment_end": "15.08.2020 19:11:00 UTC",
- "lastUpdateReason": "DOORSTATECHANGED"
- },
- "vehicleMessages": {
- "ccmMessages": [],
- "cbsMessages": [
- {
- "description": "Nächster Wechsel spätestens zum angegebenen Termin.",
- "text": "Bremsflüssigkeit",
- "id": 3,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- },
- {
- "description": "Nächste Sichtprüfung nach der angegebenen Fahrstrecke oder zum angegebenen Termin.",
- "text": "Fahrzeug-Check",
- "id": 17,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- },
- {
- "description": "Nächster Wechsel nach der angegebenen Fahrstrecke oder zum angegebenen Termin.",
- "text": "Motoröl",
- "id": 1,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- },
- {
- "description": "Nächste gesetzliche Fahrzeuguntersuchung zum angegebenen Termin.",
- "text": "§ Fahrzeuguntersuchung",
- "id": 32,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- }
- ]
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "attributesMap": {
- "unitOfLength": "km",
- "sunroof_state": "CLOSED",
- "chargingLogicCurrentlyActive": "NOT_CHARGING",
- "vehicle_tracking": "1",
- "chargeNowAllowed": "ALLOWED",
- "updateTime_converted": "27.09.2020 13:18",
- "door_driver_rear": "CLOSED",
- "head_unit_pu_software": "07/16",
- "beMaxRangeElectricKm": "203.0",
- "door_passenger_rear": "CLOSED",
- "beRemainingRangeFuelKm": "65.0",
- "Segment_LastTrip_time_segment_end_formatted_date": "27.09.2020",
- "door_driver_front": "CLOSED",
- "shdStatusUnified": "CLOSED",
- "hood_state": "CLOSED",
- "charging_status": "CHARGINGERROR",
- "kombi_current_remaining_range_fuel": "65.0",
- "beMaxRangeElectric": "203.0",
- "window_driver_rear": "CLOSED",
- "beRemainingRangeElectricKm": "100.0",
- "mileage": "18313",
- "Segment_LastTrip_time_segment_end_formatted_time": "13:24",
- "beMaxRangeElectricMile": "126.0",
- "Segment_LastTrip_time_segment_end_formatted": "27.09.2020 13:24",
- "lastChargingEndResult": "FAILED",
- "check_control_messages": "00804,18312",
- "unitOfEnergy": "kWh",
- "beRemainingRangeElectric": "100.0",
- "sunroof_position": "0",
- "soc_hv_percent": "51.6",
- "single_immediate_charging": "isUnused",
- "updateTime_converted_time": "13:18",
- "chargingHVStatus": "ERROR",
- "connectorStatus": "CONNECTED",
- "chargingLevelHv": "51.0",
- "chargingSystemStatus": "CHARGINGERROR",
- "fuelPercent": "47",
- "unitOfCombustionConsumption": "l/100km",
- "gps_lat": "56.789",
- "window_driver_front": "CLOSED",
- "Segment_LastTrip_ratio_electric_driven_distance": "100",
- "gps_lng": "8.765",
- "condition_based_services": "00003,OK,2021-11,;00017,OK,2021-11,;00001,OK,2021-11,;00032,OK,2021-11,",
- "window_passenger_front": "CLOSED",
- "targetSoc": "100.0",
- "window_passenger_rear": "CLOSED",
- "lastChargingEndReason": "POWERGRID_FAILED",
- "updateTime_converted_date": "27.09.2020",
- "beRemainingRangeFuelMile": "40.0",
- "beRemainingRangeFuel": "65.0",
- "door_passenger_front": "CLOSED",
- "updateTime_converted_timestamp": "1601212738000",
- "remaining_fuel": "4",
- "charging_inductive_positioning": "not_positioned",
- "heading": "39",
- "lsc_trigger": "CHARGINGINTERRUPTED",
- "lights_parking": "OFF",
- "door_lock_state": "SECURED",
- "updateTime": "27.09.2020 13:18:58 UTC",
- "prognosisWhileChargingStatus": "NOT_NEEDED",
- "head_unit": "EntryNav",
- "trunk_state": "CLOSED",
- "battery_size_max": "33200",
- "charging_connection_type": "CONDUCTIVE",
- "beRemainingRangeElectricMile": "62.0",
- "unitOfElectricConsumption": "kWh/100km",
- "Segment_LastTrip_time_segment_end": "27.09.2020 13:24:00 UTC",
- "lastUpdateReason": "CHARGINGINTERRUPTED"
- },
- "vehicleMessages": {
- "ccmMessages": [
- {
- "text": "Laden nicht möglich",
- "id": 804,
- "status": "NULL",
- "messageType": "CCM",
- "unitOfLengthRemaining": "18312"
- }
- ],
- "cbsMessages": [
- {
- "description": "Nächster Wechsel spätestens zum angegebenen Termin.",
- "text": "Bremsflüssigkeit",
- "id": 3,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- },
- {
- "description": "Nächste Sichtprüfung nach der angegebenen Fahrstrecke oder zum angegebenen Termin.",
- "text": "Fahrzeug-Check",
- "id": 17,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- },
- {
- "description": "Nächster Wechsel nach der angegebenen Fahrstrecke oder zum angegebenen Termin.",
- "text": "Motoröl",
- "id": 1,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- },
- {
- "description": "Nächste gesetzliche Fahrzeuguntersuchung zum angegebenen Termin.",
- "text": "§ Fahrzeuguntersuchung",
- "id": 32,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- }
- ]
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "attributesMap": {
- "unitOfLength": "km",
- "sunroof_state": "CLOSED",
- "chargingLogicCurrentlyActive": "NOT_CHARGING",
- "vehicle_tracking": "1",
- "chargeNowAllowed": "NOT_ALLOWED",
- "updateTime_converted": "22.08.2020 13:55",
- "door_driver_rear": "CLOSED",
- "head_unit_pu_software": "07/16",
- "beMaxRangeElectricKm": "209.0",
- "door_passenger_rear": "CLOSED",
- "beRemainingRangeFuelKm": "67.0",
- "Segment_LastTrip_time_segment_end_formatted_date": "22.08.2020",
- "door_driver_front": "CLOSED",
- "shdStatusUnified": "CLOSED",
- "hood_state": "CLOSED",
- "charging_status": "NOCHARGING",
- "kombi_current_remaining_range_fuel": "67.0",
- "beMaxRangeElectric": "209.0",
- "window_driver_rear": "CLOSED",
- "beRemainingRangeElectricKm": "179.0",
- "mileage": "17236",
- "Segment_LastTrip_time_segment_end_formatted_time": "14:52",
- "beMaxRangeElectricMile": "129.0",
- "Segment_LastTrip_time_segment_end_formatted": "22.08.2020 14:52",
- "lastChargingEndResult": "SUCCESS",
- "check_control_messages": "",
- "unitOfEnergy": "kWh",
- "beRemainingRangeElectric": "179.0",
- "sunroof_position": "0",
- "soc_hv_percent": "82.6",
- "single_immediate_charging": "isUnused",
- "updateTime_converted_time": "13:55",
- "chargingHVStatus": "INVALID",
- "connectorStatus": "DISCONNECTED",
- "chargingLevelHv": "89.0",
- "chargingSystemStatus": "NOCHARGING",
- "fuelPercent": "47",
- "unitOfCombustionConsumption": "l/100km",
- "gps_lat": "56.789",
- "window_driver_front": "CLOSED",
- "Segment_LastTrip_ratio_electric_driven_distance": "100",
- "gps_lng": "8.765",
- "condition_based_services": "00003,OK,2021-11,;00017,OK,2021-11,;00001,OK,2021-11,;00032,OK,2021-11,",
- "window_passenger_front": "CLOSED",
- "window_passenger_rear": "CLOSED",
- "lastChargingEndReason": "CHARGING_GOAL_REACHED",
- "updateTime_converted_date": "22.08.2020",
- "beRemainingRangeFuelMile": "41.0",
- "beRemainingRangeFuel": "67.0",
- "door_passenger_front": "CLOSED",
- "updateTime_converted_timestamp": "1598104546000",
- "remaining_fuel": "4",
- "charging_inductive_positioning": "not_positioned",
- "heading": "41",
- "lsc_trigger": "VEHCSHUTDOWN_SECURED",
- "lights_parking": "OFF",
- "door_lock_state": "SECURED",
- "updateTime": "22.08.2020 12:55:46 UTC",
- "prognosisWhileChargingStatus": "NOT_NEEDED",
- "head_unit": "EntryNav",
- "trunk_state": "CLOSED",
- "battery_size_max": "33200",
- "charging_connection_type": "CONDUCTIVE",
- "beRemainingRangeElectricMile": "111.0",
- "unitOfElectricConsumption": "kWh/100km",
- "Segment_LastTrip_time_segment_end": "22.08.2020 14:52:00 UTC",
- "lastUpdateReason": "VEHCSHUTDOWN_SECURED"
- },
- "vehicleMessages": {
- "ccmMessages": [],
- "cbsMessages": [
- {
- "description": "Nächster Wechsel spätestens zum angegebenen Termin.",
- "text": "Bremsflüssigkeit",
- "id": 3,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- },
- {
- "description": "Nächste Sichtprüfung nach der angegebenen Fahrstrecke oder zum angegebenen Termin.",
- "text": "Fahrzeug-Check",
- "id": 17,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- },
- {
- "description": "Nächster Wechsel nach der angegebenen Fahrstrecke oder zum angegebenen Termin.",
- "text": "Motoröl",
- "id": 1,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- },
- {
- "description": "Nächste gesetzliche Fahrzeuguntersuchung zum angegebenen Termin.",
- "text": "§ Fahrzeuguntersuchung",
- "id": 32,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-11"
- }
- ]
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicleStatus": {
- "vin": "ANONYMOUS",
- "mileage": 17131,
- "updateReason": "VEHICLE_SECURED",
- "updateTime": "2020-08-18T17:54:12+0000",
- "doorDriverFront": "CLOSED",
- "doorDriverRear": "CLOSED",
- "doorPassengerFront": "CLOSED",
- "doorPassengerRear": "CLOSED",
- "windowDriverFront": "CLOSED",
- "windowDriverRear": "CLOSED",
- "windowPassengerFront": "CLOSED",
- "windowPassengerRear": "CLOSED",
- "sunroof": "CLOSED",
- "trunk": "CLOSED",
- "rearWindow": "INVALID",
- "hood": "CLOSED",
- "doorLockState": "SECURED",
- "parkingLight": "OFF",
- "positionLight": "ON",
- "remainingFuel": 4,
- "remainingRangeElectric": 184,
- "remainingRangeElectricMls": 114,
- "remainingRangeFuel": 72,
- "remainingRangeFuelMls": 44,
- "maxRangeElectric": 224,
- "maxRangeElectricMls": 139,
- "maxFuel": 8.5,
- "connectionStatus": "DISCONNECTED",
- "chargingStatus": "INVALID",
- "chargingLevelHv": 84,
- "lastChargingEndReason": "END_REQUESTED_BY_DRIVER",
- "lastChargingEndResult": "SUCCESS",
- "position": {
- "lat": 56.789,
- "lon": 8.765,
- "heading": 41,
- "status": "OK"
- },
- "internalDataTimeUTC": "2020-08-18T17:54:12",
- "singleImmediateCharging": false,
- "chargingConnectionType": "CONDUCTIVE",
- "chargingInductivePositioning": "NOT_POSITIONED",
- "vehicleCountry": "DE",
- "checkControlMessages": [],
- "cbsData": [
- {
- "cbsType": "BRAKE_FLUID",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next change due at the latest by the stated date."
- },
- {
- "cbsType": "VEHICLE_CHECK",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next visual inspection due when the stated distance has been covered or by the stated date."
- },
- {
- "cbsType": "OIL",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next change due when the stated distance has been covered or by the specified date."
- },
- {
- "cbsType": "VEHICLE_TUV",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next statutory vehicle inspection due by the stated date."
- }
- ],
- "DCS_CCH_Activation": "NA",
- "DCS_CCH_Ongoing": false
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "allTrips": {
- "avgElectricConsumption": {
- "communityLow": -4.74,
- "communityAverage": 5.8,
- "communityHigh": 21.04,
- "userAverage": 6.73
- },
- "avgRecuperation": {
- "communityLow": 1.27,
- "communityAverage": 5.29,
- "communityHigh": 19.77,
- "userAverage": 5.1
- },
- "chargecycleRange": {
- "communityAverage": 11.04,
- "communityHigh": 169.59,
- "userAverage": 23.74,
- "userHigh": 3.62,
- "userCurrentChargeCycle": 28.06
- },
- "totalElectricDistance": {
- "communityLow": 20.33,
- "communityAverage": 16575.07,
- "communityHigh": 63175.2,
- "userTotal": 20102.5
- },
- "avgCombinedConsumption": {
- "communityLow": 2.45,
- "communityAverage": 6.08,
- "communityHigh": 10.86,
- "userAverage": 4.93
- },
- "savedCO2": 557.382,
- "savedCO2greenEnergy": 3278.717,
- "totalSavedFuel": 1156.8,
- "resetDate": "2020-09-19T12:54:20+0000",
- "batterySizeMax": 9140
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "weeklyPlanner": {
- "climatizationEnabled": true,
- "chargingMode": "IMMEDIATE_CHARGING",
- "chargingPreferences": "CHARGING_WINDOW",
- "timer1": {
- "departureTime": "07:10",
- "timerEnabled": true,
- "weekdays": [
- "MONDAY",
- "TUESDAY",
- "WEDNESDAY",
- "THURSDAY",
- "FRIDAY"
- ]
- },
- "timer2": {
- "departureTime": "09:00",
- "timerEnabled": true,
- "weekdays": [
- "SATURDAY",
- "SUNDAY"
- ]
- },
- "timer3": {
- "departureTime": "00:00",
- "timerEnabled": false,
- "weekdays": []
- },
- "overrideTimer": {
- "departureTime": "09:00",
- "timerEnabled": false,
- "weekdays": [
- "SUNDAY"
- ]
- },
- "preferredChargingWindow": {
- "startTime": "00:00",
- "endTime": "00:00"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicles": [
- {
- "vin": "ANONYMOUS",
- "model": "530e iPerformance",
- "driveTrain": "PHEV",
- "brand": "BMW",
- "yearOfConstruction": 2018,
- "bodytype": "G30",
- "color": "SCHWARZ II",
- "statisticsCommunityEnabled": false,
- "statisticsAvailable": true,
- "hasAlarmSystem": true,
- "dealer": {
- "name": "ANONYMOUS",
- "street": "ANONYMOUS",
- "postalCode": "ANONYMOUS",
- "city": "ANONYMOUS",
- "country": "ANONYMOUS",
- "phone": "ANONYMOUS"
- },
- "breakdownNumber": "ANONYMOUS",
- "supportedChargingModes": [
- "AC_LOW"
- ],
- "chargingControl": "WEEKLY_PLANNER",
- "vehicleFinder": "ACTIVATED",
- "hornBlow": "ACTIVATED",
- "lightFlash": "ACTIVATED",
- "doorLock": "ACTIVATED",
- "doorUnlock": "ACTIVATED",
- "climateNow": "ACTIVATED",
- "sendPoi": "ACTIVATED",
- "remote360": "NOT_SUPPORTED",
- "climateControl": "NOT_SUPPORTED",
- "chargeNow": "NOT_SUPPORTED",
- "lastDestinations": "NOT_SUPPORTED",
- "carCloud": "ACTIVATED",
- "remoteSoftwareUpgrade": "NOT_SUPPORTED",
- "climateNowRES": "NOT_SUPPORTED",
- "climateControlRES": "NOT_SUPPORTED",
- "smartSolution": "NOT_SUPPORTED",
- "ipa": "NOT_SUPPORTED"
- }
- ]
-}
\ No newline at end of file
+++ /dev/null
-{
- "lastTrip": {
- "efficiencyValue": 1,
- "totalDistance": 2,
- "electricDistance": 2,
- "avgElectricConsumption": 9,
- "avgRecuperation": 9,
- "drivingModeValue": 0.5,
- "totalConsumptionValue": 1.25,
- "avgCombinedConsumption": 0,
- "electricDistanceRatio": 100,
- "savedFuel": 1.53,
- "date": "2020-09-19T16:03:00+0000",
- "duration": 3,
- "chargingBehaviorValue": 1.25,
- "electricDistanceShareValue": 1.25
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicleStatus": {
- "mileage": 47035,
- "remainingFuel": 18.0,
- "remainingRangeElectric": 3.0,
- "remainingRangeElectricMls": 1.0,
- "remainingRangeFuel": 249.0,
- "remainingRangeFuelMls": 154.0,
- "maxRangeElectric": 32.0,
- "maxRangeElectricMls": 19.0,
- "maxFuel": 0.0,
- "chargingLevelHv": 17.0,
- "vin": "ANONYMOUS",
- "updateReason": "VEHICLE_SHUTDOWN_SECURED",
- "updateTime": "2020-09-19T14:04:25+0000",
- "doorDriverFront": "CLOSED",
- "doorDriverRear": "CLOSED",
- "doorPassengerFront": "CLOSED",
- "doorPassengerRear": "CLOSED",
- "windowDriverFront": "CLOSED",
- "windowDriverRear": "CLOSED",
- "windowPassengerFront": "CLOSED",
- "windowPassengerRear": "CLOSED",
- "trunk": "CLOSED",
- "rearWindow": "INVALID",
- "hood": "CLOSED",
- "doorLockState": "SECURED",
- "parkingLight": "OFF",
- "positionLight": "OFF",
- "connectionStatus": "DISCONNECTED",
- "chargingStatus": "INVALID",
- "lastChargingEndReason": "CHARGING_GOAL_REACHED",
- "lastChargingEndResult": "SUCCESS",
- "position": {
- "lat": -1.0,
- "lon": -1.0,
- "heading": -1,
- "status": "OK"
- },
- "internalDataTimeUTC": "2020-09-19T14:04:25",
- "singleImmediateCharging": false,
- "chargingConnectionType": "CONDUCTIVE",
- "chargingInductivePositioning": "NOT_POSITIONED",
- "vehicleCountry": "SE",
- "DCS_CCH_Activation": "NA",
- "DCS_CCH_Ongoing": false,
- "checkControlMessages": [],
- "cbsData": [
- {
- "cbsType": "OIL",
- "cbsState": "OK",
- "cbsDueDate": "2022-01",
- "cbsDescription": "Next service due when the stated distance has been covered or by the specified date.",
- "cbsRemainingMileage": 19000
- },
- {
- "cbsType": "VEHICLE_CHECK",
- "cbsState": "OK",
- "cbsDueDate": "2022-01",
- "cbsDescription": "Next visual inspection due when the stated distance has been covered or by the stated date.",
- "cbsRemainingMileage": 19000
- },
- {
- "cbsType": "BRAKE_FLUID",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next change due at the latest by the stated date.",
- "cbsRemainingMileage": 0
- }
- ]
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "attributesMap": {
- "remaining_fuel": "34",
- "unitOfCombustionConsumption": "l/100km",
- "unitOfLength": "km",
- "vehicle_tracking": "0",
- "unitOfEnergy": "kWh",
- "head_unit_pu_software": "07/14",
- "condition_based_services": "00001,PENDING,2021-08,2000;00100,OK,2023-08,23000;00003,OK,2021-08,",
- "head_unit": "NBT",
- "unitOfElectricConsumption": "kWh/100km",
- "lastUpdateReason": "Error",
- "mileage": "113930"
- },
- "vehicleMessages": {
- "ccmMessages": [],
- "cbsMessages": [
- {
- "description": "Onderhoud binnenkort nodig. Maak a.u.b. een afspraak met uw Servicepartner.",
- "text": "Motorolie",
- "id": 1,
- "status": "PENDING",
- "messageType": "CBS",
- "date": "2021-08",
- "unitOfLengthRemaining": "2000"
- },
- {
- "description": "Volgende optische controle uiterlijk na afloop van de aangegeven afstand of uiterlijk op het aangegeven tijdstip",
- "text": "Voertuig check",
- "id": 100,
- "status": "OK",
- "messageType": "CBS",
- "date": "2023-08",
- "unitOfLengthRemaining": "23000"
- },
- {
- "description": "Volgende vervanging uiterlijk op het aangegeven tijdstip.",
- "text": "Remvloeistof",
- "id": 3,
- "status": "OK",
- "messageType": "CBS",
- "date": "2021-08"
- }
- ]
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicleStatus" : {
- "doorPassengerFront" : "CLOSED",
- "cbsData" : [ {
- "cbsState" : "OK",
- "cbsRemainingMileage" : 47000,
- "cbsDescription" : "Next change due at the latest when the stated distance has been covered.",
- "cbsType" : "BRAKE_PADS_FRONT"
- }, {
- "cbsState" : "OK",
- "cbsRemainingMileage" : 46000,
- "cbsDescription" : "Next change due at the latest when the stated distance has been covered.",
- "cbsType" : "BRAKE_PADS_REAR"
- }, {
- "cbsDueDate" : "2018-06",
- "cbsState" : "OK",
- "cbsRemainingMileage" : 12000,
- "cbsDescription" : "Next service due when the stated distance has been covered or by the specified date.",
- "cbsType" : "OIL"
- }, {
- "cbsDueDate" : "2021-06",
- "cbsState" : "OK",
- "cbsRemainingMileage" : 50000,
- "cbsDescription" : "Next visual inspection due when the stated distance has been covered or by the stated date.",
- "cbsType" : "VEHICLE_CHECK"
- }, {
- "cbsDueDate" : "2020-04",
- "cbsState" : "OK",
- "cbsDescription" : "Next change due at the latest by the stated date.",
- "cbsType" : "BRAKE_FLUID"
- } ],
- "windowDriverFront" : "CLOSED",
- "DCS_CCH_Ongoing" : false,
- "updateReason" : "VEHICLE_SHUTDOWN",
- "rearWindow" : "INVALID",
- "remainingRangeFuelMls" : 154,
- "DCS_CCH_Activation" : "NA",
- "singleImmediateCharging" : false,
- "positionLight" : "OFF",
- "vin" : "F15_VIN",
- "doorDriverFront" : "CLOSED",
- "mileage" : 1629,
- "checkControlMessages" : [ ],
- "parkingLight" : "OFF",
- "windowDriverRear" : "CLOSED",
- "steering" : "LH",
- "updateTime" : "2018-03-08T22:21:39-0500",
- "remainingFuel" : 30,
- "windowPassengerRear" : "CLOSED",
- "trunk" : "CLOSED",
- "hood" : "CLOSED",
- "internalDataTimeUTC" : "2018-03-09T03:21:39",
- "windowPassengerFront" : "CLOSED",
- "doorLockState" : "UNLOCKED",
- "doorPassengerRear" : "CLOSED",
- "remainingRangeFuel" : 249,
- "doorDriverRear" : "CLOSED",
- "sunroof" : "CLOSED",
- "position" : {
- "heading" : 11,
- "lon" : -23.123,
- "lat" : 23.123,
- "status" : "OK"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicleStatus": {
- "mileage": 26112,
- "remainingFuel": 55.0,
- "remainingRangeElectric": 0.0,
- "remainingRangeElectricMls": 0.0,
- "remainingRangeFuel": 879.0,
- "remainingRangeFuelMls": 546.0,
- "maxRangeElectric": 0.0,
- "maxRangeElectricMls": 0.0,
- "maxFuel": 0.0,
- "chargingLevelHv": 0.0,
- "vin": "ANONYMOUS",
- "updateReason": "DOOR_STATE_CHANGED",
- "updateTime": "2020-09-28T13:17:20+0000",
- "doorDriverFront": "CLOSED",
- "doorDriverRear": "CLOSED",
- "doorPassengerFront": "CLOSED",
- "doorPassengerRear": "CLOSED",
- "windowDriverFront": "CLOSED",
- "windowDriverRear": "CLOSED",
- "windowPassengerFront": "CLOSED",
- "windowPassengerRear": "CLOSED",
- "sunroof": "UNKOWN",
- "trunk": "CLOSED",
- "rearWindow": "CLOSED",
- "hood": "CLOSED",
- "doorLockState": "SECURED",
- "parkingLight": "OFF",
- "positionLight": "OFF",
- "position": {
- "lat": -1.0,
- "lon": -1.0,
- "heading": -1,
- "status": "OK"
- },
- "internalDataTimeUTC": "2020-09-28T13:16:36",
- "singleImmediateCharging": false,
- "vehicleCountry": "NL",
- "DCS_CCH_Activation": "NA",
- "DCS_CCH_Ongoing": false,
- "checkControlMessages": [],
- "cbsData": [
- {
- "cbsType": "OIL",
- "cbsState": "OK",
- "cbsDueDate": "2022-07",
- "cbsDescription": "Next service due when the stated distance has been covered or by the specified date.",
- "cbsRemainingMileage": 28000
- },
- {
- "cbsType": "VEHICLE_CHECK",
- "cbsState": "OK",
- "cbsDueDate": "2022-07",
- "cbsDescription": "Next visual inspection due when the stated distance has been covered or by the stated date.",
- "cbsRemainingMileage": 28000
- },
- {
- "cbsType": "BRAKE_FLUID",
- "cbsState": "OK",
- "cbsDueDate": "2022-01",
- "cbsDescription": "Next change due at the latest by the stated date.",
- "cbsRemainingMileage": -1
- }
- ]
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "DCS_CCH_Activation": "NA",
- "DCS_CCH_Ongoing": false,
- "vehicleStatus": {
- "position": {
- "lat": 12.3456,
- "lon": 34.5678,
- "status": "OK"
- },
- "steering": "RH",
- "updateTime": "2018-07-25T16:02:04+0200",
- "vin": "F31_VIN"
- }
-}
+++ /dev/null
-{
- "vehicleStatus": {
- "DCS_CCH_Activation": "NA",
- "DCS_CCH_Ongoing": false,
- "position": {
- "status": "DRIVER_DISABLED"
- },
- "updateTime": "2018-07-16T21:47:46+0000",
- "vehicleCountry": "CN",
- "vin": "some_vin"
- }
-}
+++ /dev/null
-{
- "vehicleStatus": {
- "position": {
- "lat": 12.3456,
- "lon": 34.5678,
- "status": "OK"
- },
- "steering": "LH",
- "updateTime": "2018-04-06T13:56:47+0200",
- "vin": "F45_vin"
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicleStatus" : {
- "doorPassengerFront" : "CLOSED",
- "cbsData" : [ {
- "cbsDueDate" : "2019-07",
- "cbsState" : "OK",
- "cbsRemainingMileage" : 9000,
- "cbsDescription" : "Next service due when the stated distance has been covered or by the specified date.",
- "cbsType" : "OIL"
- }, {
- "cbsDueDate" : "2021-07",
- "cbsState" : "OK",
- "cbsRemainingMileage" : 39000,
- "cbsDescription" : "Next visual inspection due when the stated distance has been covered or by the stated date.",
- "cbsType" : "VEHICLE_CHECK"
- }, {
- "cbsDueDate" : "2020-07",
- "cbsState" : "OK",
- "cbsDescription" : "Next change due at the latest by the stated date.",
- "cbsType" : "BRAKE_FLUID"
- } ],
- "windowDriverFront" : "CLOSED",
- "DCS_CCH_Ongoing" : false,
- "updateReason" : "DOOR_STATE_CHANGED",
- "rearWindow" : "INVALID",
- "remainingRangeFuelMls" : 366,
- "DCS_CCH_Activation" : "NA",
- "singleImmediateCharging" : false,
- "positionLight" : "OFF",
- "vin" : "F48_VIN",
- "doorDriverFront" : "CLOSED",
- "mileage" : 21529,
- "checkControlMessages": [
- {
- "ccmDescriptionLong": "You can continue driving. Check tyre pressure when tyres are cold and adjust if necessary. Perform reset after adjustment. See Owner's Handbook for further information.",
- "ccmDescriptionShort": "Tyre pressure notification",
- "ccmId": 955,
- "ccmMileage": 41544
- }
- ],
- "parkingLight" : "OFF",
- "windowDriverRear" : "CLOSED",
- "steering" : "LH",
- "updateTime" : "2018-03-10T19:35:30+0100",
- "remainingFuel" : 39,
- "windowPassengerRear" : "CLOSED",
- "trunk" : "CLOSED",
- "hood" : "CLOSED",
- "internalDataTimeUTC" : "2018-03-10T18:35:30",
- "windowPassengerFront" : "CLOSED",
- "doorLockState" : "SECURED",
- "doorPassengerRear" : "CLOSED",
- "remainingRangeFuel" : 590,
- "doorDriverRear" : "CLOSED",
- "position" : {
- "heading" : 141,
- "lon" : 10.1010101,
- "lat" : 50.505050,
- "status" : "OK"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "access_token": "some_token_string",
- "token_type": "Bearer",
- "expires_in": 28799,
- "refresh_token": "another_token_string",
- "scope": "authenticate_user vehicle_data remote_services"
-}
\ No newline at end of file
+++ /dev/null
-{
- "executionStatus" : {
- "serviceType" : "LIGHT_FLASH",
- "eventId" : "424C39333232312237B3E900@bmw.de",
- "extendedStatus" : {
- "result" : "STATUS_UNKNOWN"
- },
- "status" : "DELIVERED"
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "executionStatus" : {
- "serviceType" : "LIGHT_FLASH",
- "eventId" : "424C39333232312237B3E900@bmw.de",
- "extendedStatus" : {
- "result" : "STATUS_CHANGED",
- "ignitionOnStatus" : "false"
- },
- "status" : "EXECUTED"
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "executionStatus" : {
- "serviceType" : "LIGHT_FLASH",
- "eventId" : "424C39333232312237B3E900@bmw.de",
- "status" : "INITIATED"
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "executionStatus" : {
- "serviceType" : "LIGHT_FLASH",
- "eventId" : "424C39333232312237B3E900@bmw.de",
- "extendedStatus" : {
- "result" : "STATUS_UNKNOWN"
- },
- "status" : "PENDING"
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicleStatus" : {
- "doorPassengerFront" : "CLOSED",
- "cbsData" : [ {
- "cbsDueDate" : "2020-01",
- "cbsState" : "OK",
- "cbsRemainingMileage" : 25000,
- "cbsDescription" : "Next service due when the stated distance has been covered or by the specified date.",
- "cbsType" : "OIL"
- }, {
- "cbsDueDate" : "2022-01",
- "cbsState" : "OK",
- "cbsRemainingMileage" : 60000,
- "cbsDescription" : "Next visual inspection due when the stated distance has been covered or by the stated date.",
- "cbsType" : "VEHICLE_CHECK"
- }, {
- "cbsDueDate" : "2021-01",
- "cbsState" : "OK",
- "cbsDescription" : "Next change due at the latest by the stated date.",
- "cbsType" : "BRAKE_FLUID"
- } ],
- "windowDriverFront" : "CLOSED",
- "DCS_CCH_Ongoing" : false,
- "updateReason" : "VEHICLE_SHUTDOWN",
- "rearWindow" : "CLOSED",
- "remainingRangeFuelMls" : 199,
- "DCS_CCH_Activation" : "NA",
- "singleImmediateCharging" : false,
- "positionLight" : "OFF",
- "vin" : "G31_NBTevo_VIN",
- "doorDriverFront" : "CLOSED",
- "mileage" : 4126,
- "checkControlMessages" : [ ],
- "parkingLight" : "OFF",
- "windowDriverRear" : "CLOSED",
- "steering" : "LH",
- "updateTime" : "2018-03-10T11:39:41+0100",
- "remainingFuel" : 33,
- "windowPassengerRear" : "CLOSED",
- "trunk" : "CLOSED",
- "hood" : "CLOSED",
- "internalDataTimeUTC" : "2018-03-10T10:39:41",
- "windowPassengerFront" : "CLOSED",
- "doorLockState" : "SECURED",
- "doorPassengerRear" : "CLOSED",
- "remainingRangeFuel" : 321,
- "doorDriverRear" : "CLOSED",
- "position" : {
- "heading" : 174,
- "lon" : 10.1010,
- "lat" : 50.5050,
- "status" : "OK"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicleStatus" : {
- "doorPassengerFront" : "CLOSED",
- "cbsData" : [ {
- "cbsDueDate" : "2020-01",
- "cbsState" : "OK",
- "cbsRemainingMileage" : 25000,
- "cbsDescription" : "Next service due when the stated distance has been covered or by the specified date.",
- "cbsType" : "OIL"
- }, {
- "cbsDueDate" : "2022-01",
- "cbsState" : "OK",
- "cbsRemainingMileage" : 60000,
- "cbsDescription" : "Next visual inspection due when the stated distance has been covered or by the stated date.",
- "cbsType" : "VEHICLE_CHECK"
- }, {
- "cbsDueDate" : "2021-01",
- "cbsState" : "OK",
- "cbsDescription" : "Next change due at the latest by the stated date.",
- "cbsType" : "BRAKE_FLUID"
- } ],
- "windowDriverFront" : "CLOSED",
- "DCS_CCH_Ongoing" : false,
- "updateReason" : "VEHICLE_SHUTDOWN_SECURED",
- "rearWindow" : "CLOSED",
- "remainingRangeFuelMls" : 187,
- "DCS_CCH_Activation" : "NA",
- "singleImmediateCharging" : false,
- "positionLight" : "OFF",
- "vin" : "G31_VIN",
- "doorDriverFront" : "CLOSED",
- "mileage" : 4134,
- "checkControlMessages" : [ ],
- "parkingLight" : "OFF",
- "windowDriverRear" : "CLOSED",
- "steering" : "LH",
- "updateTime" : "2018-03-12T08:56:16+0100",
- "remainingFuel" : 32,
- "windowPassengerRear" : "CLOSED",
- "trunk" : "CLOSED",
- "hood" : "CLOSED",
- "internalDataTimeUTC" : "2018-03-12T07:56:16",
- "windowPassengerFront" : "CLOSED",
- "doorLockState" : "SECURED",
- "doorPassengerRear" : "CLOSED",
- "remainingRangeFuel" : 302,
- "doorDriverRear" : "CLOSED",
- "position" : {
- "status" : "DRIVER_DISABLED"
- }
- }
-}
+++ /dev/null
-{
- "vehicleStatus": {
- "doorPassengerFront": "CLOSED",
- "cbsData": [
- {
- "cbsDueDate": "2018-11",
- "cbsState": "OK",
- "cbsDescription": "Next change due at the latest by the stated date.",
- "cbsType": "BRAKE_FLUID"
- },
- {
- "cbsDueDate": "2018-11",
- "cbsState": "OK",
- "cbsDescription": "Next visual inspection due when the stated distance has been covered or by the stated date.",
- "cbsType": "VEHICLE_CHECK"
- }
- ],
- "windowDriverFront": "CLOSED",
- "DCS_CCH_Ongoing": false,
- "updateReason": "VEHICLE_SHUTDOWN_SECURED",
- "rearWindow": "INVALID",
- "DCS_CCH_Activation": "NA",
- "chargingInductivePositioning": "NOT_POSITIONED",
- "remainingRangeElectric": 53,
- "singleImmediateCharging": false,
- "maxRangeElectric": 103,
- "chargingConnectionType": "CONDUCTIVE",
- "positionLight": "OFF",
- "lastChargingEndReason": "END_REQUESTED_BY_DRIVER",
- "chargingStatus": "INVALID",
- "vin": "I01_NOREX_VIN",
- "doorDriverFront": "CLOSED",
- "mileage": 16059,
- "checkControlMessages": [],
- "parkingLight": "OFF",
- "windowDriverRear": "CLOSED",
- "remainingRangeElectricMls": 32,
- "lastChargingEndResult": "SUCCESS",
- "steering": "RH",
- "updateTime": "2018-03-15T15:44:30+0100",
- "remainingFuel": 0,
- "windowPassengerRear": "CLOSED",
- "trunk": "CLOSED",
- "hood": "CLOSED",
- "internalDataTimeUTC": "2018-03-15T14:44:30",
- "maxRangeElectricMls": 64,
- "windowPassengerFront": "CLOSED",
- "doorLockState": "SECURED",
- "doorPassengerRear": "CLOSED",
- "doorDriverRear": "CLOSED",
- "sunroof": "CLOSED",
- "connectionStatus": "DISCONNECTED",
- "chargingLevelHv": 58,
- "position": {
- "heading": 123,
- "lon": 12.3456,
- "lat": -65.4321,
- "status": "OK"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "allTrips": {
- "avgElectricConsumption": {
- "communityLow": 11.05,
- "communityAverage": 16.28,
- "communityHigh": 21.99,
- "userAverage": 16.46
- },
- "avgRecuperation": {
- "communityLow": 0.47,
- "communityAverage": 3.37,
- "communityHigh": 11.51,
- "userAverage": 4.53
- },
- "chargecycleRange": {
- "communityAverage": 194.21,
- "communityHigh": 270,
- "userAverage": 57.3,
- "userHigh": 185.48,
- "userCurrentChargeCycle": 68
- },
- "totalElectricDistance": {
- "communityLow": 19,
- "communityAverage": 40850.56,
- "communityHigh": 193006,
- "userTotal": 16629.4
- },
- "avgCombinedConsumption": {
- "communityLow": 0,
- "communityAverage": 0.92,
- "communityHigh": 4.44,
- "userAverage": 0.64
- },
- "savedCO2": 461.083,
- "savedCO2greenEnergy": 2712.255,
- "totalSavedFuel": 0,
- "resetDate": "2020-08-24T14:40:40+0000",
- "batterySizeMax": 33200
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "weeklyPlanner": {
- "climatizationEnabled": true,
- "chargingMode": "IMMEDIATE_CHARGING",
- "chargingPreferences": "CHARGING_WINDOW",
- "timer1": {
- "departureTime": "05:00",
- "timerEnabled": false,
- "weekdays": [
- "MONDAY",
- "TUESDAY",
- "WEDNESDAY",
- "THURSDAY",
- "FRIDAY"
- ]
- },
- "timer2": {
- "departureTime": "12:00",
- "timerEnabled": true,
- "weekdays": [
- "SATURDAY"
- ]
- },
- "timer3": {
- "departureTime": "00:00",
- "timerEnabled": false,
- "weekdays": []
- },
- "overrideTimer": {
- "departureTime": "12:00",
- "timerEnabled": false,
- "weekdays": [
- "SATURDAY"
- ]
- },
- "preferredChargingWindow": {
- "startTime": "11:00",
- "endTime": "17:00"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicles": [
- {
- "vin": "MY_REAL_VIN",
- "model": "i3 94 (+ REX)",
- "bodytype": "I01",
- "driveTrain": "BEV_REX",
- "color": "CAPPARISWEISS MIT AKZENT BMW I BLAU",
- "colorCode": "B85",
- "brand": "BMW_I",
- "yearOfConstruction": 2017,
- "statisticsCommunityEnabled": false,
- "statisticsAvailable": true,
- "hub": "HUB_ECE",
- "hasAlarmSystem": true,
- "dealer": {
- "name": "My Real Dealer Name",
- "street": "My Real Dealer Address",
- "postalCode": "4711",
- "city": "My Real Dealer City",
- "country": "DE",
- "phone": "My Real Dealer Phone"
- },
- "breakdownNumber": "Real Phone Number",
- "countryCode": "V6",
- "egoVehiclePath": "",
- "chargingUpdateMode": "NORMAL_PROGNOSE_BASED",
- "steering": "LH",
- "vehicleFinderRestriction": "NONE",
- "hmiVersion": "ID4",
- "a4a": "USB_ONLY",
- "vehicleFinder": "ACTIVATED",
- "remote360": "NOT_SUPPORTED",
- "hornBlow": "ACTIVATED",
- "lightFlash": "ACTIVATED",
- "doorLock": "ACTIVATED",
- "doorUnlock": "ACTIVATED",
- "climateControl": "NOT_SUPPORTED",
- "climateNow": "ACTIVATED",
- "climateNowRES": "NOT_SUPPORTED",
- "climateControlRES": "NOT_SUPPORTED",
- "chargingControl": "WEEKLY_PLANNER",
- "chargeNow": "NOT_SUPPORTED",
- "sendPoi": "ACTIVATED",
- "rangeMap": "RANGE_CIRCLE",
- "lastDestinations": "SUPPORTED",
- "intermodalRouting": "NOT_AVAILABLE",
- "climateFunction": "AIRCONDITIONING",
- "onlineSearchMode": "MAP",
- "onlineSearchProvider": "GOOGLE",
- "smartSolution": "NOT_SUPPORTED",
- "carCloud": "NOT_SUPPORTED",
- "supportedChargingModes": [
- "AC_LOW",
- "DC"
- ],
- "lscType": "I_LSC_IMM",
- "ipa": "NOT_SUPPORTED",
- "puStep": "1119",
- "remoteSoftwareUpgrade": "NOT_SUPPORTED"
- }
- ]
-}
\ No newline at end of file
+++ /dev/null
-{
- "lastTrip": {
- "efficiencyValue": 0.98,
- "totalDistance": 2,
- "electricDistance": 2,
- "avgElectricConsumption": 7,
- "avgRecuperation": 6,
- "drivingModeValue": 0.87,
- "totalConsumptionValue": 1.25,
- "avgCombinedConsumption": 0,
- "electricDistanceRatio": 100,
- "savedFuel": 0,
- "date": "2020-08-24T17:55:00+0000",
- "duration": 5,
- "auxiliaryConsumptionValue": 0.78,
- "anticipationValue": 0.99,
- "accelerationValue": 0.99
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicleStatus" : {
- "doorPassengerFront" : "CLOSED",
- "cbsData" : [ {
- "cbsDueDate" : "2018-05",
- "cbsState" : "OK",
- "cbsDescription" : "Next change due at the latest by the stated date.",
- "cbsType" : "BRAKE_FLUID"
- }, {
- "cbsDueDate" : "2018-05",
- "cbsState" : "OK",
- "cbsDescription" : "Next visual inspection due when the stated distance has been covered or by the stated date.",
- "cbsType" : "VEHICLE_CHECK"
- } ],
- "windowDriverFront" : "CLOSED",
- "DCS_CCH_Ongoing" : false,
- "updateReason" : "VEHICLE_SHUTDOWN_SECURED",
- "rearWindow" : "INVALID",
- "remainingRangeFuelMls" : 65,
- "DCS_CCH_Activation" : "NA",
- "chargingInductivePositioning" : "NOT_POSITIONED",
- "remainingRangeElectric" : 48,
- "singleImmediateCharging" : false,
- "maxRangeElectric" : 94,
- "chargingConnectionType" : "CONDUCTIVE",
- "positionLight" : "OFF",
- "lastChargingEndReason" : "UNKNOWN",
- "chargingStatus" : "CHARGING",
- "vin" : "I01_VIN",
- "doorDriverFront" : "CLOSED",
- "mileage" : 38807,
- "chargingTimeRemaining" : 332,
- "checkControlMessages" : [ ],
- "parkingLight" : "OFF",
- "windowDriverRear" : "CLOSED",
- "remainingRangeElectricMls" : 30,
- "lastChargingEndResult" : "UNKNOWN",
- "steering" : "LH",
- "updateTime" : "2018-03-12T08:38:57+0100",
- "remainingFuel" : 8,
- "windowPassengerRear" : "CLOSED",
- "trunk" : "CLOSED",
- "hood" : "CLOSED",
- "internalDataTimeUTC" : "2018-03-12T06:21:01",
- "maxRangeElectricMls" : 58,
- "maxFuel" : 8,
- "windowPassengerFront" : "CLOSED",
- "doorLockState" : "SECURED",
- "doorPassengerRear" : "CLOSED",
- "remainingRangeFuel" : 106,
- "doorDriverRear" : "CLOSED",
- "connectionStatus" : "CONNECTED",
- "chargingLevelHv" : 54,
- "position" : {
- "heading" : 356,
- "lon" : 15.00000,
- "lat" : 58.000000,
- "status" : "OK"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "redirect_to": "redirect_uri=com.bmw.connected://oauth?client_id=31c357a0-7a1d-4590-aa99-33b97244d048&response_type=code&scope=openid profile email offline_access smacc vehicle_data perseus dlm svds cesim vsapi remote_services fupo authenticate_user&state=cEG9eLAIi6Nv-aaCAniziE_B6FPoobva3qr5gukilYw&authorization=XaTJvSCZePkXsQ3zLMbPyG2XpRo.*AAJTSQACMDIAAlNLABw2TmhkS25qQTQzc1lqUHdOYzNjanFZK1pkU2M9AAR0eXBlAANDVFMAAlMxAAIwMQ..*"
-}
\ No newline at end of file
+++ /dev/null
-{
- "token_type": "Bearer",
- "access_token": "Iw-U6XS5zSeArLauaI-Ec6WFs88",
- "refresh_token": "V3OAHd_foseD2nzTFV5_SsaMzGU",
- "scope": "smacc vehicle_data perseus dlm svds openid profile vsapi remote_services authenticate_user cesim offline_access email fupo",
- "expires_in": 3599,
- "id_token": "eyJ0eXAiOiJKV1QiLCJraWQiOiIydGFUMUlOdTJFVE1QZFd4UWpIR3UyV3Q2T0E9IiwiYWxnIjoiUlMyNTYifQ.eyJhdF9oYXNoIjoiTGh1ZGZhT0pUOTBvYlNjYVhuN2RQUSIsInN1YiI6Im1hcmlrYS53ZXltYW5uQGdtYWlsLmNvbSIsImF1ZGl0VHJhY2tpbmdJZCI6ImJlNjcxM2M3LTY4NjgtNGU4My04NjIyLTg4ODMyNjg2MmU1OC0yODg4MDcwNDYiLCJnY2lkIjoiZDdjNTU5NjctNzQ5ZC00NjNiLTlhN2UtMTQ3ZGEwMmZiMzQ0IiwiaXNzIjoiaHR0cHM6Ly9jdXN0b21lci5ibXdncm91cC5jb20vYW0vb2F1dGgyIiwidG9rZW5OYW1lIjoiaWRfdG9rZW4iLCJhY3IiOiIwIiwiYXpwIjoiMzFjMzU3YTAtN2ExZC00NTkwLWFhOTktMzNiOTcyNDRkMDQ4IiwiYXV0aF90aW1lIjoxNjMwODYxOTE3LCJleHAiOjE2MzA4NjU1MTcsImlhdCI6MTYzMDg2MTkxNywiZW1haWwiOiJtYXJpa2Eud2V5bWFubkBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6Ik1BSUxfQUNUSVZFIiwiaG9tZV9tYXJrZXQiOiJERSIsImdpdmVuX25hbWUiOiJNYXJpa2EiLCJub25jZSI6ImxvZ2luX25vbmNlIiwiYXVkIjoiMzFjMzU3YTAtN2ExZC00NTkwLWFhOTktMzNiOTcyNDRkMDQ4IiwiY19oYXNoIjoiZVU5MjYyRTZiLUFlNzFfWHd2eWkwdyIsIm9yZy5mb3JnZXJvY2sub3BlbmlkY29ubmVjdC5vcHMiOiJwLWFnaGZMdlh1S29IcnNReTd1Z05xVEQyVEkiLCJzX2hhc2giOiJwcXpwa0pfS09mQ2htTTg4dFVLcExRIiwicmVhbG0iOiIvY3VzdG9tZXIiLCJzYWx1dGF0aW9uIjoiU0FMX01TIiwidG9rZW5UeXBlIjoiSldUVG9rZW4iLCJmYW1pbHlfbmFtZSI6IldleW1hbm4ifQ.LJxHE4BeUNh0YxhMIyF_LUa8hsAaGZ2VZot15vp_5SQWQvfGoC0KMgjuHawc-7CK01yDppR5awX2FwCsec3DemSUVvKeyjSg_of785dvCNsvcx9kvio-7nwet_6Acrv0bUlmpOtvN6GZpxE6NZi-ZkbEnw8KzrZvS8t6AgAv7dEeqPgVneZNu9XDSUM81QhS1X21FFGbyPD-9RnLt401Ft5WeKi4kN1ViCP7OkvpSOfRU3p4lv3fbsdoAoWU11Lp80TBYir8nJL-kykA076UK6qnks8zTFx1TlpPV0Nou5NgmqyLOprFaWk-9AG3gjhEYC2yLBMzQLHb8t2UYgAfUQ"
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicles": [
- {
- "vehicleFinderRestriction": "NONE",
- "doorUnlock": "ACTIVATED",
- "color": "SOPHISTOGRAU BRILLANTEFFEKT METALLI",
- "onlineSearchMode": "MAP",
- "breakdownNumber": "+4912345678",
- "smartSolution": "NOT_SUPPORTED",
- "yearOfConstruction": 2018,
- "driveTrain": "CONV",
- "rangeMap": "NOT_SUPPORTED",
- "vehicleFinder": "ACTIVATED",
- "a4a": "BLUETOOTH",
- "hornBlow": "ACTIVATED",
- "chargeNow": "NOT_SUPPORTED",
- "countryCode": "V6",
- "bodytype": "G31",
- "lightFlash": "ACTIVATED",
- "vin": "G31_NBTevo_VIN",
- "model": "530i xDrive",
- "ipa": "NOT_SUPPORTED",
- "brand": "BMW",
- "climateControl": "DEPARTURE_TIMER",
- "sendPoi": "ACTIVATED",
- "lastDestinations": "NOT_SUPPORTED",
- "doorLock": "ACTIVATED",
- "chargingControl": "NOT_SUPPORTED",
- "onlineSearchProvider": "GOOGLE",
- "steering": "LH",
- "intermodalRouting": "NOT_AVAILABLE",
- "lscType": "LSC_BASIS",
- "hasAlarmSystem": true,
- "statisticsCommunityEnabled": false,
- "climateNow": "ACTIVATED",
- "fuelType": "PETROL",
- "hub": "HUB_ECE",
- "remote360": "NOT_SUPPORTED",
- "hmiVersion": "ID5",
- "climateFunction": "PARK_HEATING",
- "colorCode": "A90",
- "carCloud": "ACTIVATED",
- "statisticsAvailable": false
- },
- {
- "vehicleFinderRestriction": "NONE",
- "doorUnlock": "ACTIVATED",
- "color": "ALPINWEISS III",
- "onlineSearchMode": "MAP",
- "breakdownNumber": "+4989358111111",
- "smartSolution": "NOT_SUPPORTED",
- "yearOfConstruction": 2017,
- "driveTrain": "CONV",
- "rangeMap": "NOT_SUPPORTED",
- "vehicleFinder": "ACTIVATED",
- "a4a": "BLUETOOTH",
- "hornBlow": "ACTIVATED",
- "chargeNow": "NOT_SUPPORTED",
- "countryCode": "V1-NL",
- "bodytype": "F48",
- "lightFlash": "ACTIVATED",
- "vin": "F48_VIN",
- "model": "X1 sDrive18i",
- "ipa": "NOT_SUPPORTED",
- "brand": "BMW",
- "climateControl": "START_TIMER",
- "sendPoi": "ACTIVATED",
- "lastDestinations": "NOT_SUPPORTED",
- "doorLock": "ACTIVATED",
- "chargingControl": "NOT_SUPPORTED",
- "onlineSearchProvider": "GOOGLE",
- "steering": "LH",
- "intermodalRouting": "NOT_AVAILABLE",
- "lscType": "LSC_BASIS",
- "hasAlarmSystem": true,
- "statisticsCommunityEnabled": false,
- "climateNow": "ACTIVATED",
- "fuelType": "PETROL",
- "hub": "HUB_ECE",
- "remote360": "NOT_SUPPORTED",
- "hmiVersion": "ID5",
- "dealer": {
- "country": "NL",
- "city": "city",
- "phone": "phone",
- "street": "street",
- "postalCode": "1234 AB",
- "name": "BMW dealer"
- },
- "climateFunction": "VENTILATION",
- "colorCode": "300",
- "carCloud": "ACTIVATED",
- "statisticsAvailable": false
- },
- {
- "vehicleFinderRestriction": "NONE",
- "doorUnlock": "ACTIVATED",
- "color": "ARRAVANIGRAU/BMW i BLAU",
- "onlineSearchMode": "MAP",
- "breakdownNumber": "+4989358957103",
- "smartSolution": "NOT_SUPPORTED",
- "yearOfConstruction": 2014,
- "driveTrain": "BEV_REX",
- "rangeMap": "RANGE_CIRCLE",
- "vehicleFinder": "ACTIVATED",
- "a4a": "USB_ONLY",
- "hornBlow": "ACTIVATED",
- "chargeNow": "NOT_SUPPORTED",
- "licensePlate": "ABC000",
- "countryCode": "V1-R1-SE",
- "bodytype": "I01",
- "lightFlash": "ACTIVATED",
- "vin": "I01_VIN",
- "model": "i3 (+ REX)",
- "ipa": "NOT_SUPPORTED",
- "brand": "BMW_I",
- "climateControl": "NOT_SUPPORTED",
- "sendPoi": "ACTIVATED",
- "lastDestinations": "SUPPORTED",
- "doorLock": "ACTIVATED",
- "chargingControl": "WEEKLY_PLANNER",
- "onlineSearchProvider": "GOOGLE",
- "steering": "LH",
- "intermodalRouting": "NOT_AVAILABLE",
- "supportedChargingModes": [
- "AC_LOW",
- "AC_HIGH",
- "DC"
- ],
- "lscType": "I_LSC_IMM",
- "hasAlarmSystem": false,
- "statisticsCommunityEnabled": false,
- "climateNow": "ACTIVATED",
- "hub": "HUB_ECE",
- "remote360": "NOT_SUPPORTED",
- "hmiVersion": "ID4",
- "dealer": {
- "country": "SE",
- "city": "Solna",
- "phone": "+46 8 7353900",
- "street": "G�rdsv�gen 9-11",
- "postalCode": "169 70",
- "name": "Bavaria Sverige Bil AB/ Solna"
- },
- "climateFunction": "AIRCONDITIONING",
- "colorCode": "B74",
- "carCloud": "NOT_SUPPORTED",
- "statisticsAvailable": true
- },
- {
- "vehicleFinderRestriction": "NONE",
- "doorUnlock": "NOT_SUPPORTED",
- "color": "BLACK SAPPHIRE METALLIC",
- "onlineSearchMode": "MAP",
- "breakdownNumber": "+4989358957103",
- "smartSolution": "NOT_SUPPORTED",
- "yearOfConstruction": 2017,
- "driveTrain": "CONV",
- "rangeMap": "NOT_SUPPORTED",
- "vehicleFinder": "ACTIVATED",
- "a4a": "BLUETOOTH",
- "hornBlow": "ACTIVATED",
- "chargeNow": "NOT_SUPPORTED",
- "countryCode": "V2-CA",
- "bodytype": "F15",
- "lightFlash": "ACTIVATED",
- "vin": "F15_VIN",
- "model": "X5 xDrive35i",
- "ipa": "NOT_SUPPORTED",
- "brand": "BMW",
- "climateControl": "DEPARTURE_TIMER",
- "sendPoi": "ACTIVATED",
- "lastDestinations": "NOT_SUPPORTED",
- "doorLock": "ACTIVATED",
- "chargingControl": "NOT_SUPPORTED",
- "steering": "LH",
- "intermodalRouting": "NOT_AVAILABLE",
- "lscType": "LSC_BASIS",
- "hasAlarmSystem": true,
- "statisticsCommunityEnabled": false,
- "climateNow": "ACTIVATED",
- "fuelType": "PETROL",
- "hub": "HUB_US",
- "remote360": "NOT_SUPPORTED",
- "hmiVersion": "ID5",
- "dealer": {
- "country": "CA",
- "city": "Ottawa",
- "phone": "1-866-599-4999",
- "street": "85 Wellington St",
- "postalCode": "K1A 1A1",
- "name": "Parliament of Canada"
- },
- "climateFunction": "VENTILATION",
- "colorCode": "475",
- "carCloud": "NOT_SUPPORTED",
- "statisticsAvailable": false
- },
- {
- "vehicleFinderRestriction": "NONE",
- "doorUnlock": "ACTIVATED",
- "color": "SOLARORANGE MET. M. AKZENT FROZEN G",
- "onlineSearchMode": "MAP",
- "breakdownNumber": "+4989358957103",
- "smartSolution": "NOT_SUPPORTED",
- "yearOfConstruction": 2014,
- "driveTrain": "BEV",
- "rangeMap": "RANGE_CIRCLE",
- "vehicleFinder": "ACTIVATED",
- "a4a": "USB_ONLY",
- "hornBlow": "ACTIVATED",
- "chargeNow": "NOT_SUPPORTED",
- "licensePlate": "HIDDEN",
- "countryCode": "B3-ZA",
- "bodytype": "I01",
- "lightFlash": "ACTIVATED",
- "vin": "I01_NOREX_VIN",
- "model": "i3",
- "ipa": "NOT_SUPPORTED",
- "brand": "BMW_I",
- "climateControl": "NOT_SUPPORTED",
- "sendPoi": "ACTIVATED",
- "lastDestinations": "SUPPORTED",
- "doorLock": "ACTIVATED",
- "chargingControl": "WEEKLY_PLANNER",
- "onlineSearchProvider": "GOOGLE",
- "steering": "RH",
- "intermodalRouting": "NOT_AVAILABLE",
- "supportedChargingModes": [
- "AC_LOW",
- "AC_HIGH"
- ],
- "lscType": "I_LSC_IMM",
- "hasAlarmSystem": true,
- "statisticsCommunityEnabled": true,
- "climateNow": "ACTIVATED",
- "hub": "HUB_ECE",
- "remote360": "NOT_SUPPORTED",
- "hmiVersion": "ID4",
- "dealer": {
- "country": "ZA",
- "city": "Midrand",
- "phone": "+27 12 522 3000",
- "street": "1 Bavaria Avenue",
- "postalCode": "1685",
- "name": "BMW (South Africa) (Pty) Ltd. ZA"
- },
- "climateFunction": "AIRCONDITIONING",
- "colorCode": "B78",
- "carCloud": "NOT_SUPPORTED",
- "statisticsAvailable": true
- },
- {
- "a4a": "USB_ONLY",
- "bodytype": "F45",
- "brand": "BMW",
- "breakdownNumber": "+4989358957103",
- "carCloud": "NOT_SUPPORTED",
- "chargeNow": "NOT_SUPPORTED",
- "chargingControl": "NOT_SUPPORTED",
- "climateControl": "START_TIMER",
- "climateFunction": "VENTILATION",
- "climateNow": "ACTIVATED",
- "color": "MEDITERRANBLAU METALLIC",
- "colorCode": "C10",
- "countryCode": "V1-ES",
- "dealer": {
- "city": "Madrid",
- "country": "ES",
- "name": "BMW Iberica S.A.",
- "phone": "+34 913350505",
- "postalCode": "28050",
- "street": "Avenida de Burgos ,118"
- },
- "doorLock": "ACTIVATED",
- "doorUnlock": "ACTIVATED",
- "driveTrain": "CONV",
- "fuelType": "PETROL",
- "hasAlarmSystem": false,
- "hmiVersion": "ID4",
- "hornBlow": "ACTIVATED",
- "hub": "HUB_ECE",
- "intermodalRouting": "NOT_AVAILABLE",
- "ipa": "NOT_SUPPORTED",
- "lastDestinations": "NOT_SUPPORTED",
- "licensePlate": "some_license_plate",
- "lightFlash": "ACTIVATED",
- "lscType": "NOT_SUPPORTED",
- "model": "225i",
- "onlineSearchMode": "MAP",
- "onlineSearchProvider": "GOOGLE",
- "rangeMap": "NOT_SUPPORTED",
- "remote360": "NOT_SUPPORTED",
- "sendPoi": "ACTIVATED",
- "smartSolution": "NOT_SUPPORTED",
- "statisticsAvailable": false,
- "statisticsCommunityEnabled": false,
- "steering": "LH",
- "vehicleFinder": "ACTIVATED",
- "vehicleFinderRestriction": "NONE",
- "vin": "F45_VIN",
- "yearOfConstruction": 2016
- },
- {
- "a4a": "USB_ONLY",
- "bodytype": "F31",
- "brand": "BMW",
- "breakdownNumber": "+4989358957103",
- "carCloud": "NOT_SUPPORTED",
- "chargeNow": "NOT_SUPPORTED",
- "chargingControl": "NOT_SUPPORTED",
- "climateControl": "START_TIMER",
- "climateControlRES": "NOT_SUPPORTED",
- "climateFunction": "VENTILATION",
- "climateNow": "ACTIVATED",
- "climateNowRES": "NOT_SUPPORTED",
- "color": "PLATINSILBER METALLIC",
- "colorCode": "C08",
- "countryCode": "V1-UK",
- "dealer": {
- "city": "Farnborough",
- "country": "GB",
- "name": "BMW (UK) Ltd. ICS - DIRECT SUPPLY",
- "phone": "+44 1252 920000",
- "postalCode": "GU14 0FB",
- "street": "Summit ONE"
- },
- "doorLock": "ACTIVATED",
- "doorUnlock": "ACTIVATED",
- "driveTrain": "CONV",
- "fuelType": "DIESEL",
- "hasAlarmSystem": true,
- "hmiVersion": "ID4",
- "hornBlow": "NOT_SUPPORTED",
- "hub": "HUB_ECE",
- "intermodalRouting": "NOT_AVAILABLE",
- "ipa": "NOT_SUPPORTED",
- "lastDestinations": "NOT_SUPPORTED",
- "licensePlate": "some_license_plate",
- "lightFlash": "ACTIVATED",
- "lscType": "NOT_SUPPORTED",
- "model": "320d",
- "onlineSearchMode": "MAP",
- "onlineSearchProvider": "GOOGLE",
- "rangeMap": "NOT_SUPPORTED",
- "remote360": "NOT_SUPPORTED",
- "remoteSoftwareUpgrade": "NOT_SUPPORTED",
- "sendPoi": "ACTIVATED",
- "smartSolution": "NOT_SUPPORTED",
- "statisticsAvailable": false,
- "statisticsCommunityEnabled": false,
- "steering": "RH",
- "vehicleFinder": "ACTIVATED",
- "vehicleFinderRestriction": "NONE",
- "vin": "F31_VIN",
- "yearOfConstruction": 2015
- },
- {
- "a4a": "NOT_SUPPORTED",
- "bodytype": "F35",
- "brand": "BMW",
- "breakdownNumber": "+4989358957103",
- "carCloud": "NOT_SUPPORTED",
- "chargeNow": "NOT_SUPPORTED",
- "chargingControl": "NOT_SUPPORTED",
- "climateControl": "NOT_SUPPORTED",
- "climateControlRES": "NOT_SUPPORTED",
- "climateFunction": "VENTILATION",
- "climateNow": "NOT_SUPPORTED",
- "climateNowRES": "NOT_SUPPORTED",
- "color": "MINERALWEISS METALLIC",
- "colorCode": "A96",
- "countryCode": "V5-CN",
- "dealer": {
- "city": "Beijing",
- "country": "CN",
- "name": "Beijing Baozen Baiwang Automotive Sales Co., Ltd.",
- "phone": "+86 10 62826789",
- "postalCode": "100094",
- "street": "F2 Baiwang Green Valley"
- },
- "doorLock": "NOT_SUPPORTED",
- "doorUnlock": "NOT_SUPPORTED",
- "driveTrain": "CONV",
- "fuelType": "PETROL",
- "hasAlarmSystem": false,
- "hmiVersion": "ID5",
- "hornBlow": "NOT_SUPPORTED",
- "hub": "HUB_CN",
- "intermodalRouting": "NOT_AVAILABLE",
- "ipa": "NOT_SUPPORTED",
- "lastDestinations": "NOT_SUPPORTED",
- "licensePlate": "some_license_plate",
- "lightFlash": "NOT_SUPPORTED",
- "lscType": "NOT_SUPPORTED",
- "model": "328Li",
- "onlineSearchMode": "MAP",
- "rangeMap": "NOT_SUPPORTED",
- "remote360": "NOT_SUPPORTED",
- "remoteSoftwareUpgrade": "NOT_SUPPORTED",
- "sendPoi": "NOT_SUPPORTED",
- "smartSolution": "NOT_SUPPORTED",
- "statisticsAvailable": false,
- "statisticsCommunityEnabled": false,
- "steering": "LH",
- "vehicleFinder": "NOT_SUPPORTED",
- "vin": "F31_VIN",
- "yearOfConstruction": 2015
- },
- {
- "vin": "ANONYMOUS",
- "model": "318i",
- "driveTrain": "CONV",
- "brand": "BMW",
- "yearOfConstruction": 2019,
- "bodytype": "F31",
- "color": "MINERALGRAU METALLIC",
- "statisticsCommunityEnabled": false,
- "statisticsAvailable": false,
- "hasAlarmSystem": true,
- "dealer": {
- "name": "ANONYMOUS",
- "street": "ANONYMOUS",
- "postalCode": "ANONYMOUS",
- "city": "ANONYMOUS",
- "country": "ANONYMOUS",
- "phone": "ANONYMOUS"
- },
- "breakdownNumber": "ANONYMOUS",
- "chargingControl": "NOT_SUPPORTED",
- "vehicleFinder": "ACTIVATED",
- "hornBlow": "ACTIVATED",
- "lightFlash": "ACTIVATED",
- "doorLock": "ACTIVATED",
- "doorUnlock": "ACTIVATED",
- "climateNow": "ACTIVATED",
- "sendPoi": "ACTIVATED",
- "remote360": "NOT_SUPPORTED",
- "climateControl": "START_TIMER",
- "chargeNow": "NOT_SUPPORTED",
- "lastDestinations": "NOT_SUPPORTED",
- "carCloud": "ACTIVATED",
- "remoteSoftwareUpgrade": "NOT_SUPPORTED",
- "climateNowRES": "NOT_SUPPORTED",
- "climateControlRES": "NOT_SUPPORTED",
- "smartSolution": "NOT_SUPPORTED",
- "ipa": "NOT_SUPPORTED"
- }
- ]
-}
+++ /dev/null
-{
- "allTrips": {
- "avgElectricConsumption": {
- "communityLow": 11.05,
- "communityAverage": 16.28,
- "communityHigh": 21.99,
- "userAverage": 16.46
- },
- "avgRecuperation": {
- "communityLow": 0.47,
- "communityAverage": 3.37,
- "communityHigh": 11.51,
- "userAverage": 4.53
- },
- "chargecycleRange": {
- "communityAverage": 194.21,
- "communityHigh": 270,
- "userAverage": 57.3,
- "userHigh": 185.48,
- "userCurrentChargeCycle": 68
- },
- "totalElectricDistance": {
- "communityLow": 19,
- "communityAverage": 40850.56,
- "communityHigh": 193006,
- "userTotal": 16629.4
- },
- "avgCombinedConsumption": {
- "communityLow": 0,
- "communityAverage": 0.92,
- "communityHigh": 4.44,
- "userAverage": 0.64
- },
- "savedCO2": 461.083,
- "savedCO2greenEnergy": 2712.255,
- "totalSavedFuel": 0,
- "resetDate": "2020-08-24T14:40:40+0000",
- "batterySizeMax": 33200
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "weeklyPlanner": {
- "climatizationEnabled": true,
- "chargingMode": "IMMEDIATE_CHARGING",
- "chargingPreferences": "CHARGING_WINDOW",
- "timer1": {
- "departureTime": "05:00",
- "timerEnabled": false,
- "weekdays": [
- "MONDAY",
- "TUESDAY",
- "WEDNESDAY",
- "THURSDAY",
- "FRIDAY"
- ]
- },
- "timer2": {
- "departureTime": "12:00",
- "timerEnabled": true,
- "weekdays": [
- "SATURDAY"
- ]
- },
- "timer3": {
- "departureTime": "00:00",
- "timerEnabled": false,
- "weekdays": []
- },
- "overrideTimer": {
- "departureTime": "12:00",
- "timerEnabled": false,
- "weekdays": [
- "SATURDAY"
- ]
- },
- "preferredChargingWindow": {
- "startTime": "11:00",
- "endTime": "17:00"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicles": [
- {
- "vin": "MY_REAL_VIN",
- "model": "i3 94 (+ REX)",
- "bodytype": "I01",
- "driveTrain": "BEV_REX",
- "color": "CAPPARISWEISS MIT AKZENT BMW I BLAU",
- "colorCode": "B85",
- "brand": "BMW_I",
- "yearOfConstruction": 2017,
- "statisticsCommunityEnabled": false,
- "statisticsAvailable": true,
- "hub": "HUB_ECE",
- "hasAlarmSystem": true,
- "dealer": {
- "name": "My Real Dealer Name",
- "street": "My Real Dealer Address",
- "postalCode": "4711",
- "city": "My Real Dealer City",
- "country": "DE",
- "phone": "My Real Dealer Phone"
- },
- "breakdownNumber": "Real Phone Number",
- "countryCode": "V6",
- "egoVehiclePath": "",
- "chargingUpdateMode": "NORMAL_PROGNOSE_BASED",
- "steering": "LH",
- "vehicleFinderRestriction": "NONE",
- "hmiVersion": "ID4",
- "a4a": "USB_ONLY",
- "vehicleFinder": "ACTIVATED",
- "remote360": "NOT_SUPPORTED",
- "hornBlow": "ACTIVATED",
- "lightFlash": "ACTIVATED",
- "doorLock": "ACTIVATED",
- "doorUnlock": "ACTIVATED",
- "climateControl": "NOT_SUPPORTED",
- "climateNow": "ACTIVATED",
- "climateNowRES": "NOT_SUPPORTED",
- "climateControlRES": "NOT_SUPPORTED",
- "chargingControl": "WEEKLY_PLANNER",
- "chargeNow": "NOT_SUPPORTED",
- "sendPoi": "ACTIVATED",
- "rangeMap": "RANGE_CIRCLE",
- "lastDestinations": "SUPPORTED",
- "intermodalRouting": "NOT_AVAILABLE",
- "climateFunction": "AIRCONDITIONING",
- "onlineSearchMode": "MAP",
- "onlineSearchProvider": "GOOGLE",
- "smartSolution": "NOT_SUPPORTED",
- "carCloud": "NOT_SUPPORTED",
- "supportedChargingModes": [
- "AC_LOW",
- "DC"
- ],
- "lscType": "I_LSC_IMM",
- "ipa": "NOT_SUPPORTED",
- "puStep": "1119",
- "remoteSoftwareUpgrade": "NOT_SUPPORTED"
- }
- ]
-}
\ No newline at end of file
+++ /dev/null
-{
- "destinations": [
- {
- "lat": 47.11,
- "lon": 47.11,
- "country": "DEUTSCHLAND",
- "city": "My Real City",
- "street": "My Real Street",
- "streetNumber": "My Real Number",
- "type": "DESTINATION",
- "createdAt": "2020-08-16T12:52:58+0000"
- },
- {
- "lat": 47.11,
- "lon": 47.11,
- "country": "DEUTSCHLAND",
- "city": "My Real City",
- "street": "My Real Street",
- "streetNumber": "My Real Number",
- "type": "DESTINATION",
- "createdAt": "2020-08-12T17:03:35+0000"
- },
- {
- "lat": 47.11,
- "lon": 47.11,
- "country": "DEUTSCHLAND",
- "city": "My Real City",
- "street": "My Real Street",
- "streetNumber": "My Real Number",
- "type": "DESTINATION",
- "createdAt": "2020-08-03T08:15:20+0000"
- },
- {
- "lat": 47.11,
- "lon": 47.11,
- "country": "DEUTSCHLAND",
- "city": "My Real City",
- "street": "My Real Street",
- "streetNumber": "My Real Number",
- "type": "DESTINATION",
- "createdAt": "2020-07-31T13:09:15+0000"
- },
- {
- "lat": 47.11,
- "lon": 47.11,
- "country": "DEUTSCHLAND",
- "city": "My Real City",
- "street": "My Real Street",
- "streetNumber": "My Real Number",
- "type": "DESTINATION",
- "createdAt": "2020-07-25T11:20:18+0000"
- },
- {
- "lat": 47.11,
- "lon": 47.11,
- "country": "DEUTSCHLAND",
- "city": "My Real City",
- "street": "My Real Street",
- "streetNumber": "My Real Number",
- "type": "DESTINATION",
- "createdAt": "2020-07-18T11:22:37+0000"
- },
- {
- "lat": 47.11,
- "lon": 47.11,
- "country": "DEUTSCHLAND",
- "city": "My Real City",
- "street": "My Real Street",
- "streetNumber": "My Real Number",
- "type": "DESTINATION",
- "createdAt": "2020-02-08T11:06:52+0000"
- },
- {
- "lat": 47.11,
- "lon": 47.11,
- "country": "DEUTSCHLAND",
- "city": "My Real City",
- "street": "My Real Street",
- "streetNumber": "My Real Number",
- "type": "DESTINATION",
- "createdAt": "2020-02-02T14:07:54+0000"
- },
- {
- "lat": 47.11,
- "lon": 47.11,
- "country": "DEUTSCHLAND",
- "city": "My Real City",
- "street": "My Real Street",
- "streetNumber": "My Real Number",
- "type": "DESTINATION",
- "createdAt": "2020-02-02T13:24:36+0000"
- }
- ]
-}
\ No newline at end of file
+++ /dev/null
-{
- "lastTrip": {
- "efficiencyValue": 0.98,
- "totalDistance": 2,
- "electricDistance": 2,
- "avgElectricConsumption": 7,
- "avgRecuperation": 6,
- "drivingModeValue": 0.87,
- "totalConsumptionValue": 1.25,
- "avgCombinedConsumption": 0,
- "electricDistanceRatio": 100,
- "savedFuel": 0,
- "date": "2020-08-24T17:55:00+0000",
- "duration": 5,
- "auxiliaryConsumptionValue": 0.78,
- "anticipationValue": 0.99,
- "accelerationValue": 0.99
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "executionStatus": {
- "serviceType": "DOOR_UNLOCK",
- "status": "DELIVERED",
- "eventId": "5639303536333926DA7B9400@bmw.de",
- "extendedStatus": {
- "result": "STATUS_UNKNOWN"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "executionStatus": {
- "serviceType": "DOOR_UNLOCK",
- "status": "EXECUTED",
- "eventId": "5639303536333926DA7B9400@bmw.de",
- "extendedStatus": {
- "newDoorStatus": "INVALID",
- "oldDoorStatus": "INVALID",
- "result": "STATUS_NOT_CHANGED"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "executionStatus": {
- "serviceType": "DOOR_UNLOCK",
- "status": "PENDING",
- "eventId": "5639303536333926DA7B9400@bmw.de",
- "extendedStatus": {
- "result": "STATUS_UNKNOWN"
- }
- }
-}
+++ /dev/null
-{
- "vehicleStatus": {
- "mileage": 18390,
- "remainingFuel": 4.0,
- "remainingRangeElectric": 67.0,
- "remainingRangeElectricMls": 41.0,
- "remainingRangeFuel": 59.0,
- "remainingRangeFuelMls": 36.0,
- "maxRangeElectric": 211.0,
- "maxRangeElectricMls": 131.0,
- "maxFuel": 8.5,
- "chargingLevelHv": 35.0,
- "vin": "ANONYMOUS",
- "updateReason": "VEHICLE_SHUTDOWN_SECURED",
- "updateTime": "2020-09-29T12:52:12+0000",
- "doorDriverFront": "CLOSED",
- "doorDriverRear": "CLOSED",
- "doorPassengerFront": "CLOSED",
- "doorPassengerRear": "CLOSED",
- "windowDriverFront": "CLOSED",
- "windowDriverRear": "CLOSED",
- "windowPassengerFront": "CLOSED",
- "windowPassengerRear": "CLOSED",
- "sunroof": "CLOSED",
- "trunk": "CLOSED",
- "rearWindow": "INVALID",
- "hood": "CLOSED",
- "doorLockState": "SECURED",
- "parkingLight": "OFF",
- "positionLight": "ON",
- "connectionStatus": "DISCONNECTED",
- "chargingStatus": "INVALID",
- "lastChargingEndReason": "END_REQUESTED_BY_DRIVER",
- "lastChargingEndResult": "SUCCESS",
- "position": {
- "lat": -1.0,
- "lon": -1.0,
- "heading": -1,
- "status": "OK"
- },
- "internalDataTimeUTC": "2020-09-29T12:52:12",
- "singleImmediateCharging": false,
- "chargingConnectionType": "CONDUCTIVE",
- "chargingInductivePositioning": "NOT_POSITIONED",
- "vehicleCountry": "DE",
- "DCS_CCH_Activation": "NA",
- "DCS_CCH_Ongoing": false,
- "checkControlMessages": [
- {
- "ccmDescriptionLong": "Check tyre pressure and correct if necessary. Afterwards, perform Tyre Pressure Monitor (RDC) reset. See Owner\u0027s Handbook for more information.",
- "ccmDescriptionShort": "Check tyre pressure. Initialise RDC",
- "ccmId": 142,
- "ccmMileage": 18384
- }
- ],
- "cbsData": [
- {
- "cbsType": "BRAKE_FLUID",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next change due at the latest by the stated date.",
- "cbsRemainingMileage": -1
- },
- {
- "cbsType": "VEHICLE_CHECK",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next visual inspection due when the stated distance has been covered or by the stated date.",
- "cbsRemainingMileage": -1
- },
- {
- "cbsType": "OIL",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next change due when the stated distance has been covered or by the specified date.",
- "cbsRemainingMileage": -1
- },
- {
- "cbsType": "VEHICLE_TUV",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next statutory vehicle inspection due by the stated date.",
- "cbsRemainingMileage": -1
- }
- ]
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicleStatus": {
- "vin": "ANONYMOUS",
- "mileage": 17307,
- "updateReason": "CHARGING_STARTED",
- "updateTime": "2020-08-25T15:37:16+0000",
- "doorDriverFront": "CLOSED",
- "doorDriverRear": "CLOSED",
- "doorPassengerFront": "CLOSED",
- "doorPassengerRear": "CLOSED",
- "windowDriverFront": "CLOSED",
- "windowDriverRear": "CLOSED",
- "windowPassengerFront": "CLOSED",
- "windowPassengerRear": "CLOSED",
- "sunroof": "CLOSED",
- "trunk": "CLOSED",
- "rearWindow": "INVALID",
- "hood": "CLOSED",
- "doorLockState": "SECURED",
- "parkingLight": "OFF",
- "positionLight": "ON",
- "remainingFuel": 4,
- "remainingRangeElectric": 182,
- "remainingRangeElectricMls": 113,
- "remainingRangeFuel": 69,
- "remainingRangeFuelMls": 42,
- "maxRangeElectric": 213,
- "maxRangeElectricMls": 132,
- "maxFuel": 8.5,
- "connectionStatus": "CONNECTED",
- "chargingStatus": "CHARGING",
- "chargingTimeRemaining": 76,
- "chargingLevelHv": 86,
- "lastChargingEndReason": "UNKNOWN",
- "lastChargingEndResult": "UNKNOWN",
- "position": {
- "lat": 56.789,
- "lon": 8.765,
- "heading": 41,
- "status": "OK"
- },
- "internalDataTimeUTC": "2020-08-25T12:57:59",
- "singleImmediateCharging": false,
- "chargingConnectionType": "CONDUCTIVE",
- "chargingInductivePositioning": "NOT_POSITIONED",
- "vehicleCountry": "DE",
- "checkControlMessages": [],
- "cbsData": [
- {
- "cbsType": "BRAKE_FLUID",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next change due at the latest by the stated date."
- },
- {
- "cbsType": "VEHICLE_CHECK",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next visual inspection due when the stated distance has been covered or by the stated date."
- },
- {
- "cbsType": "OIL",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next change due when the stated distance has been covered or by the specified date."
- },
- {
- "cbsType": "VEHICLE_TUV",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next statutory vehicle inspection due by the stated date."
- }
- ],
- "DCS_CCH_Activation": "NA",
- "DCS_CCH_Ongoing": false
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "vehicleStatus": {
- "vin": "ANONYMOUS",
- "mileage": 17273,
- "updateReason": "VEHICLE_SHUTDOWN_SECURED",
- "updateTime": "2020-08-24T15:55:32+0000",
- "doorDriverFront": "CLOSED",
- "doorDriverRear": "CLOSED",
- "doorPassengerFront": "CLOSED",
- "doorPassengerRear": "CLOSED",
- "windowDriverFront": "CLOSED",
- "windowDriverRear": "CLOSED",
- "windowPassengerFront": "CLOSED",
- "windowPassengerRear": "CLOSED",
- "sunroof": "CLOSED",
- "trunk": "CLOSED",
- "rearWindow": "INVALID",
- "hood": "CLOSED",
- "doorLockState": "SECURED",
- "parkingLight": "OFF",
- "positionLight": "ON",
- "remainingFuel": 4,
- "remainingRangeElectric": 148,
- "remainingRangeElectricMls": 91,
- "remainingRangeFuel": 70,
- "remainingRangeFuelMls": 43,
- "maxRangeElectric": 216,
- "maxRangeElectricMls": 134,
- "maxFuel": 8.5,
- "connectionStatus": "DISCONNECTED",
- "chargingStatus": "INVALID",
- "chargingLevelHv": 71,
- "lastChargingEndReason": "CHARGING_GOAL_REACHED",
- "lastChargingEndResult": "SUCCESS",
- "position": {
- "lat": 56.789,
- "lon": 8.765,
- "heading": 219,
- "status": "OK"
- },
- "internalDataTimeUTC": "2020-08-24T15:55:32",
- "singleImmediateCharging": false,
- "chargingConnectionType": "CONDUCTIVE",
- "chargingInductivePositioning": "NOT_POSITIONED",
- "vehicleCountry": "DE",
- "checkControlMessages": [],
- "cbsData": [
- {
- "cbsType": "BRAKE_FLUID",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next change due at the latest by the stated date.",
- "cbsRemainingMileage": 15345
- },
- {
- "cbsType": "VEHICLE_CHECK",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next visual inspection due when the stated distance has been covered or by the stated date."
- },
- {
- "cbsType": "OIL",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next change due when the stated distance has been covered or by the specified date."
- },
- {
- "cbsType": "VEHICLE_TUV",
- "cbsState": "OK",
- "cbsDueDate": "2021-11",
- "cbsDescription": "Next statutory vehicle inspection due by the stated date."
- }
- ],
- "DCS_CCH_Activation": "NA",
- "DCS_CCH_Ongoing": false
- }
-}
\ No newline at end of file
<module>org.openhab.binding.bluetooth.govee</module>
<module>org.openhab.binding.bluetooth.roaming</module>
<module>org.openhab.binding.bluetooth.ruuvitag</module>
- <module>org.openhab.binding.bmwconnecteddrive</module>
<module>org.openhab.binding.boschindego</module>
<module>org.openhab.binding.boschshc</module>
<module>org.openhab.binding.bosesoundtouch</module>