BMW ConnectedDrive binding removal (#12634)
Signed-off-by: Bernd Weymann <bernd.weymann@gmail.com>
@ -43,7 +43,6 @@
|
||||
/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
|
||||
|
@ -206,11 +206,6 @@
|
||||
<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>
|
||||
|
@ -1,13 +0,0 @@
|
||||
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
|
@ -1,998 +0,0 @@
|
||||
# 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!
|
Before Width: | Height: | Size: 211 KiB |
Before Width: | Height: | Size: 460 KiB |
Before Width: | Height: | Size: 188 KiB |
Before Width: | Height: | Size: 337 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 345 KiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 740 KiB |
@ -1,17 +0,0 @@
|
||||
<?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>
|
@ -1,9 +0,0 @@
|
||||
<?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>
|
@ -1,45 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,207 +0,0 @@
|
||||
/**
|
||||
* 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";
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,406 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* 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"}}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/**
|
||||
* 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"
|
||||
* ] '
|
||||
*/
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* 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"}}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* 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"
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* 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"
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
/**
|
||||
* 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",
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/**
|
||||
* 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",
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 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",
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* 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",
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/**
|
||||
* 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>();
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* 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",
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -1,469 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -1,264 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,544 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,819 +0,0 @@
|
||||
/**
|
||||
* 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));
|
||||
});
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
/**
|
||||
* 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";
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
}
|
@ -1,299 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
};
|
||||
}
|
@ -1,302 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/**
|
||||
* 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";
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<?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>
|
@ -1,34 +0,0 @@
|
||||
<?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>
|
@ -1,45 +0,0 @@
|
||||
<?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>
|
@ -1,253 +0,0 @@
|
||||
# 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
|
@ -1,278 +0,0 @@
|
||||
# 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
|
@ -1,12 +0,0 @@
|
||||
<?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>
|
@ -1,49 +0,0 @@
|
||||
<?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>
|
@ -1,208 +0,0 @@
|
||||
<?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>
|
@ -1,19 +0,0 @@
|
||||
<?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>
|
@ -1,15 +0,0 @@
|
||||
<?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>
|
@ -1,16 +0,0 @@
|
||||
<?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>
|
@ -1,14 +0,0 @@
|
||||
<?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>
|
@ -1,14 +0,0 @@
|
||||
<?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>
|
@ -1,66 +0,0 @@
|
||||
<?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>
|
@ -1,24 +0,0 @@
|
||||
<?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>
|
@ -1,18 +0,0 @@
|
||||
<?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>
|
@ -1,16 +0,0 @@
|
||||
<?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>
|
@ -1,19 +0,0 @@
|
||||
<?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>
|
@ -1,23 +0,0 @@
|
||||
<?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>
|
@ -1,19 +0,0 @@
|
||||
<?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>
|
@ -1,17 +0,0 @@
|
||||
<?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>
|
@ -1,25 +0,0 @@
|
||||
<?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>
|
@ -1,15 +0,0 @@
|
||||
<?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>
|
@ -1,28 +0,0 @@
|
||||
<?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>
|
@ -1,45 +0,0 @@
|
||||
<?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>
|
@ -1,34 +0,0 @@
|
||||
<?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>
|