[renault] Renault more channels and HVAC ON / toggle charge mode (#12095)
* [renault] Add more channels to Renault Binding. * [renault] Improve example rule to reduce API use. Signed-off-by: Culnane Douglas <douglas.culnane@extern.a1.at>
This commit is contained in:
parent
223eb5e24d
commit
605c06107f
@ -1,43 +1,132 @@
|
|||||||
# Renault Binding
|
# Renault Binding
|
||||||
|
|
||||||
This binding allow MyRenault App. users to get battery status and other data from their cars.
|
This binding allows MyRenault App. users to get battery status and other data from their cars.
|
||||||
|
They can also heat their cars by turning ON the HVAC status and toggle the car's charging mode.
|
||||||
|
|
||||||
A binding that translates the [python based renault-api](https://renault-api.readthedocs.io/en/latest/) in an easy to use binding.
|
The binding translates the [python based renault-api](https://renault-api.readthedocs.io/en/latest/) in an easy to use openHAB java binding.
|
||||||
|
|
||||||
|
|
||||||
## Supported Things
|
## Supported Things
|
||||||
|
|
||||||
Supports MyRenault registered cars with an active Connected-Services account.
|
Supports MyRenault registered cars with an active Connected-Services account.
|
||||||
|
|
||||||
This binding can only retrieve information that is available in the the MyRenault App.
|
This binding can only retrieve information that is available in the MyRenault App.
|
||||||
|
|
||||||
|
|
||||||
## Discovery
|
## Discovery
|
||||||
|
|
||||||
No discovery
|
No discovery
|
||||||
|
|
||||||
|
|
||||||
## Thing Configuration
|
## Thing Configuration
|
||||||
|
|
||||||
You require your MyRenault credential, locale and VIN for your MyRenault registered car.
|
You require your MyRenault credential, locale and VIN for your MyRenault registered car.
|
||||||
|
|
||||||
| Parameter | Description | Required |
|
| Parameter | Description | Required |
|
||||||
|-------------------|----------------------------------------|----------|
|
|-------------------|----------------------------------------------------------------------------|----------|
|
||||||
| myRenaultUsername | MyRenault Username. | yes |
|
| myRenaultUsername | MyRenault Username. | yes |
|
||||||
| myRenaultPassword | MyRenault Password. | yes |
|
| myRenaultPassword | MyRenault Password. | yes |
|
||||||
| locale | MyRenault Location (language_country). | yes |
|
| locale | MyRenault Location (language_country). | yes |
|
||||||
| vin | Vehicle Identification Number. | yes |
|
| vin | Vehicle Identification Number. | yes |
|
||||||
| refreshInterval | Interval the car is polled in minutes. | no |
|
| refreshInterval | Interval the car is polled in minutes. | no |
|
||||||
|
| updateDelay | How long to wait for commands to reach car and update to server in seconds.| no |
|
||||||
|
|
||||||
|
|
||||||
## Channels
|
## Channels
|
||||||
|
|
||||||
Currently all available channels are read only:
|
| Channel ID | Type | Description | Read Only |
|
||||||
|
|------------------------|--------------------|-------------------------------------------------|-----------|
|
||||||
| Channel ID | Type | Description |
|
| batteryavailableEnergy | Number:Energy | Battery Energy Available | Yes |
|
||||||
|--------------|---------------|---------------------------------|
|
| batterylevel | Number | State of the battery in % | Yes |
|
||||||
| batterylevel | Number | State of the battery in % |
|
| chargingmode | String | Charging mode. always_charging or schedule_mode | No |
|
||||||
| hvacstatus | Switch | HVAC status switch |
|
| chargingstatus | String | Charging status | Yes |
|
||||||
| image | String | Image URL of MyRenault |
|
| chargingremainingtime | Number:Time | Charging time remaining | Yes |
|
||||||
| location | Location | The GPS position of the vehicle |
|
| plugstatus | String | Status of charging plug | Yes |
|
||||||
| odometer | Number:Length | Total distance travelled |
|
| estimatedrange | Number:Length | Estimated range of the car | Yes |
|
||||||
|
| odometer | Number:Length | Total distance travelled | Yes |
|
||||||
|
| hvacstatus | String | HVAC status HVAC Status (ON, OFF, PENDING) | No |
|
||||||
|
| hvactargettemperature | Number:Temperature | HVAC target temperature (19 to 21) | No |
|
||||||
|
| externaltemperature | Number:Temperature | Temperature outside of the car | Yes |
|
||||||
|
| image | String | Image URL of MyRenault | Yes |
|
||||||
|
| location | Location | The GPS position of the vehicle | Yes |
|
||||||
|
| locationupdated | DateTime | Timestamp of the last location update | Yes |
|
||||||
|
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
Some channels may not work depending on your car and MyRenault account.
|
||||||
|
|
||||||
|
The "externaltemperature" only works on a few cars.
|
||||||
|
|
||||||
|
The "hvactargettemperature" is used by the hvacstatus ON command for pre-conditioning the car.
|
||||||
|
This seams to only allow values 19, 20 and 21 or else the pre-conditioning command will not work.
|
||||||
|
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
renaultcar.sitemap:
|
||||||
|
|
||||||
|
```
|
||||||
|
sitemap renaultcar label="Renault Car" {
|
||||||
|
Frame {
|
||||||
|
Image item=RenaultCar_ImageURL
|
||||||
|
Default item=RenaultCar_BatteryLevel icon="batterylevel"
|
||||||
|
Default item=RenaultCar_BatteryEnergyAvailable icon="energy"
|
||||||
|
Default item=RenaultCar_PlugStatus icon="poweroutlet"
|
||||||
|
Default item=RenaultCar_ChargingStatus icon="switch"
|
||||||
|
Selection item=RenaultCar_ChargingMode mappings=[SCHEDULE_MODE="Schedule mode",ALWAYS_CHARGING="Instant charge"] icon="switch"
|
||||||
|
Default item=RenaultCar_ChargingTimeRemaining icon="time"
|
||||||
|
Default item=RenaultCar_EstimatedRange
|
||||||
|
Default item=RenaultCar_Odometer
|
||||||
|
Selection item=RenaultCar_HVACStatus mappings=[ON="ON"] icon="switch"
|
||||||
|
Setpoint item=RenaultCar_HVACTargetTemperature minValue=19 maxValue=21 step=1 icon="temperature"
|
||||||
|
Default item=RenaultCar_LocationUpdate icon="time"
|
||||||
|
Default item=RenaultCar_Location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If you do not have a smart charger and want to limit the charge of the battery you can set up an active 15 minute charge schedule in the MyRenault App.
|
||||||
|
Then create a Dimmer item "RenaultCar_ChargeLimit" and set it to 80% for example. This rule will change the RenaultCar_ChargingMode to schedule_mode when the limit is reached.
|
||||||
|
This stops charging after the battery level reaches the charge limit.
|
||||||
|
|
||||||
|
ChargeRenaultCarLimit Code
|
||||||
|
|
||||||
|
```
|
||||||
|
configuration: {}
|
||||||
|
triggers:
|
||||||
|
- id: "1"
|
||||||
|
configuration:
|
||||||
|
itemName: RenaultCar_BatteryLevel
|
||||||
|
type: core.ItemStateUpdateTrigger
|
||||||
|
- id: "3"
|
||||||
|
configuration:
|
||||||
|
itemName: RenaultCar_ChargeLimit
|
||||||
|
type: core.ItemStateUpdateTrigger
|
||||||
|
- id: "4"
|
||||||
|
configuration:
|
||||||
|
itemName: RenaultCar_PlugStatus
|
||||||
|
type: core.ItemStateUpdateTrigger
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- inputs: {}
|
||||||
|
id: "2"
|
||||||
|
configuration:
|
||||||
|
type: application/vnd.openhab.dsl.rule
|
||||||
|
script: >-
|
||||||
|
if ( RenaultCar_PlugStatus.state.toString == 'PLUGGED' ) {
|
||||||
|
if ( RenaultCar_BatteryLevel.state as Number >= RenaultCar_ChargeLimit.state as Number ) {
|
||||||
|
if (RenaultCar_ChargingMode.state.toString == 'ALWAYS_CHARGING' ) {
|
||||||
|
RenaultCar_ChargingMode.sendCommand("SCHEDULE_MODE")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (RenaultCar_ChargingMode.state.toString == 'SCHEDULE_MODE' ) {
|
||||||
|
RenaultCar_ChargingMode.sendCommand("ALWAYS_CHARGING")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type: script.ScriptAction
|
||||||
|
|
||||||
|
```
|
||||||
|
|||||||
BIN
bundles/org.openhab.binding.renault/doc/sitemap.png
Normal file
BIN
bundles/org.openhab.binding.renault/doc/sitemap.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 211 KiB |
@ -30,9 +30,18 @@ public class RenaultBindingConstants {
|
|||||||
public static final ThingTypeUID THING_TYPE_CAR = new ThingTypeUID(BINDING_ID, "car");
|
public static final ThingTypeUID THING_TYPE_CAR = new ThingTypeUID(BINDING_ID, "car");
|
||||||
|
|
||||||
// List of all Channel ids
|
// List of all Channel ids
|
||||||
|
public static final String CHANNEL_BATTERY_AVAILABLE_ENERGY = "batteryavailableenergy";
|
||||||
public static final String CHANNEL_BATTERY_LEVEL = "batterylevel";
|
public static final String CHANNEL_BATTERY_LEVEL = "batterylevel";
|
||||||
|
public static final String CHANNEL_CHARGING_MODE = "chargingmode";
|
||||||
|
public static final String CHANNEL_CHARGING_STATUS = "chargingstatus";
|
||||||
|
public static final String CHANNEL_CHARGING_REMAINING_TIME = "chargingremainingtime";
|
||||||
|
public static final String CHANNEL_ESTIMATED_RANGE = "estimatedrange";
|
||||||
|
public static final String CHANNEL_EXTERNAL_TEMPERATURE = "externaltemperature";
|
||||||
public static final String CHANNEL_HVAC_STATUS = "hvacstatus";
|
public static final String CHANNEL_HVAC_STATUS = "hvacstatus";
|
||||||
|
public static final String CHANNEL_HVAC_TARGET_TEMPERATURE = "hvactargettemperature";
|
||||||
public static final String CHANNEL_IMAGE = "image";
|
public static final String CHANNEL_IMAGE = "image";
|
||||||
public static final String CHANNEL_LOCATION = "location";
|
public static final String CHANNEL_LOCATION = "location";
|
||||||
|
public static final String CHANNEL_LOCATION_UPDATED = "locationupdated";
|
||||||
public static final String CHANNEL_ODOMETER = "odometer";
|
public static final String CHANNEL_ODOMETER = "odometer";
|
||||||
|
public static final String CHANNEL_PLUG_STATUS = "plugstatus";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,4 +27,5 @@ public class RenaultConfiguration {
|
|||||||
public String locale = "";
|
public String locale = "";
|
||||||
public String vin = "";
|
public String vin = "";
|
||||||
public int refreshInterval = 10;
|
public int refreshInterval = 10;
|
||||||
|
public int updateDelay = 30;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,10 @@ import com.google.gson.JsonObject;
|
|||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class Car {
|
public class Car {
|
||||||
|
|
||||||
|
public static final String HVAC_STATUS_ON = "ON";
|
||||||
|
public static final String HVAC_STATUS_OFF = "OFF";
|
||||||
|
public static final String HVAC_STATUS_PENDING = "PENDING";
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(Car.class);
|
private final Logger logger = LoggerFactory.getLogger(Car.class);
|
||||||
|
|
||||||
private boolean disableLocation = false;
|
private boolean disableLocation = false;
|
||||||
@ -37,29 +41,90 @@ public class Car {
|
|||||||
private boolean disableCockpit = false;
|
private boolean disableCockpit = false;
|
||||||
private boolean disableHvac = false;
|
private boolean disableHvac = false;
|
||||||
|
|
||||||
|
private ChargingStatus chargingStatus = ChargingStatus.UNKNOWN;
|
||||||
|
private ChargingMode chargingMode = ChargingMode.UNKNOWN;
|
||||||
|
private PlugStatus plugStatus = PlugStatus.UNKNOWN;
|
||||||
|
private double hvacTargetTemperature = 20.0;
|
||||||
private @Nullable Double batteryLevel;
|
private @Nullable Double batteryLevel;
|
||||||
|
private @Nullable Double batteryAvailableEnergy;
|
||||||
|
private @Nullable Integer chargingRemainingTime;
|
||||||
private @Nullable Boolean hvacstatus;
|
private @Nullable Boolean hvacstatus;
|
||||||
private @Nullable Double odometer;
|
private @Nullable Double odometer;
|
||||||
|
private @Nullable Double estimatedRange;
|
||||||
private @Nullable String imageURL;
|
private @Nullable String imageURL;
|
||||||
|
private @Nullable String locationUpdated;
|
||||||
private @Nullable Double gpsLatitude;
|
private @Nullable Double gpsLatitude;
|
||||||
private @Nullable Double gpsLongitude;
|
private @Nullable Double gpsLongitude;
|
||||||
|
private @Nullable Double externalTemperature;
|
||||||
|
|
||||||
|
public enum ChargingMode {
|
||||||
|
UNKNOWN,
|
||||||
|
SCHEDULE_MODE,
|
||||||
|
ALWAYS_CHARGING
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PlugStatus {
|
||||||
|
UNPLUGGED,
|
||||||
|
PLUGGED,
|
||||||
|
PLUG_ERROR,
|
||||||
|
PLUG_UNKNOWN,
|
||||||
|
UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ChargingStatus {
|
||||||
|
NOT_IN_CHARGE,
|
||||||
|
WAITING_FOR_A_PLANNED_CHARGE,
|
||||||
|
CHARGE_ENDED,
|
||||||
|
WAITING_FOR_CURRENT_CHARGE,
|
||||||
|
ENERGY_FLAP_OPENED,
|
||||||
|
CHARGE_IN_PROGRESS,
|
||||||
|
CHARGE_ERROR,
|
||||||
|
UNAVAILABLE,
|
||||||
|
UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
public void setBatteryStatus(JsonObject responseJson) {
|
public void setBatteryStatus(JsonObject responseJson) {
|
||||||
try {
|
try {
|
||||||
JsonObject attributes = getAttributes(responseJson);
|
JsonObject attributes = getAttributes(responseJson);
|
||||||
if (attributes != null && attributes.get("batteryLevel") != null) {
|
if (attributes != null) {
|
||||||
batteryLevel = attributes.get("batteryLevel").getAsDouble();
|
if (attributes.get("batteryLevel") != null) {
|
||||||
|
batteryLevel = attributes.get("batteryLevel").getAsDouble();
|
||||||
|
}
|
||||||
|
if (attributes.get("batteryAutonomy") != null) {
|
||||||
|
estimatedRange = attributes.get("batteryAutonomy").getAsDouble();
|
||||||
|
}
|
||||||
|
if (attributes.get("plugStatus") != null) {
|
||||||
|
plugStatus = mapPlugStatus(attributes.get("plugStatus").getAsString());
|
||||||
|
}
|
||||||
|
if (attributes.get("chargingStatus") != null) {
|
||||||
|
chargingStatus = mapChargingStatus(attributes.get("chargingStatus").getAsString());
|
||||||
|
}
|
||||||
|
if (attributes.get("batteryAvailableEnergy") != null) {
|
||||||
|
batteryAvailableEnergy = attributes.get("batteryAvailableEnergy").getAsDouble();
|
||||||
|
}
|
||||||
|
if (attributes.get("chargingRemainingTime") != null) {
|
||||||
|
chargingRemainingTime = attributes.get("chargingRemainingTime").getAsInt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IllegalStateException | ClassCastException e) {
|
} catch (IllegalStateException | ClassCastException e) {
|
||||||
logger.warn("Error {} parsing Battery Status: {}", e.getMessage(), responseJson);
|
logger.warn("Error {} parsing Battery Status: {}", e.getMessage(), responseJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetHVACStatus() {
|
||||||
|
this.hvacstatus = null;
|
||||||
|
}
|
||||||
|
|
||||||
public void setHVACStatus(JsonObject responseJson) {
|
public void setHVACStatus(JsonObject responseJson) {
|
||||||
try {
|
try {
|
||||||
JsonObject attributes = getAttributes(responseJson);
|
JsonObject attributes = getAttributes(responseJson);
|
||||||
if (attributes != null && attributes.get("hvacStatus") != null) {
|
if (attributes != null) {
|
||||||
hvacstatus = attributes.get("hvacStatus").getAsString().equals("on");
|
if (attributes.get("hvacStatus") != null) {
|
||||||
|
hvacstatus = attributes.get("hvacStatus").getAsString().equals("on");
|
||||||
|
}
|
||||||
|
if (attributes.get("externalTemperature") != null) {
|
||||||
|
externalTemperature = attributes.get("externalTemperature").getAsDouble();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IllegalStateException | ClassCastException e) {
|
} catch (IllegalStateException | ClassCastException e) {
|
||||||
logger.warn("Error {} parsing HVAC Status: {}", e.getMessage(), responseJson);
|
logger.warn("Error {} parsing HVAC Status: {}", e.getMessage(), responseJson);
|
||||||
@ -87,6 +152,9 @@ public class Car {
|
|||||||
if (attributes.get("gpsLongitude") != null) {
|
if (attributes.get("gpsLongitude") != null) {
|
||||||
gpsLongitude = attributes.get("gpsLongitude").getAsDouble();
|
gpsLongitude = attributes.get("gpsLongitude").getAsDouble();
|
||||||
}
|
}
|
||||||
|
if (attributes.get("lastUpdateTime") != null) {
|
||||||
|
locationUpdated = attributes.get("lastUpdateTime").getAsString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IllegalStateException | ClassCastException e) {
|
} catch (IllegalStateException | ClassCastException e) {
|
||||||
logger.warn("Error {} parsing Location: {}", e.getMessage(), responseJson);
|
logger.warn("Error {} parsing Location: {}", e.getMessage(), responseJson);
|
||||||
@ -128,80 +196,112 @@ public class Car {
|
|||||||
return disableLocation;
|
return disableLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDisableLocation(boolean disableLocation) {
|
|
||||||
this.disableLocation = disableLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDisableBattery() {
|
public boolean isDisableBattery() {
|
||||||
return disableBattery;
|
return disableBattery;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDisableBattery(boolean disableBattery) {
|
|
||||||
this.disableBattery = disableBattery;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDisableCockpit() {
|
public boolean isDisableCockpit() {
|
||||||
return disableCockpit;
|
return disableCockpit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDisableCockpit(boolean disableCockpit) {
|
|
||||||
this.disableCockpit = disableCockpit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDisableHvac() {
|
public boolean isDisableHvac() {
|
||||||
return disableHvac;
|
return disableHvac;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDisableHvac(boolean disableHvac) {
|
|
||||||
this.disableHvac = disableHvac;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable Double getBatteryLevel() {
|
public @Nullable Double getBatteryLevel() {
|
||||||
return batteryLevel;
|
return batteryLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBatteryLevel(Double batteryLevel) {
|
|
||||||
this.batteryLevel = batteryLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable Boolean getHvacstatus() {
|
public @Nullable Boolean getHvacstatus() {
|
||||||
return hvacstatus;
|
return hvacstatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHvacstatus(Boolean hvacstatus) {
|
|
||||||
this.hvacstatus = hvacstatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable Double getOdometer() {
|
public @Nullable Double getOdometer() {
|
||||||
return odometer;
|
return odometer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOdometer(Double odometer) {
|
|
||||||
this.odometer = odometer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable String getImageURL() {
|
public @Nullable String getImageURL() {
|
||||||
return imageURL;
|
return imageURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setImageURL(String imageURL) {
|
|
||||||
this.imageURL = imageURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable Double getGpsLatitude() {
|
public @Nullable Double getGpsLatitude() {
|
||||||
return gpsLatitude;
|
return gpsLatitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGpsLatitude(Double gpsLatitude) {
|
|
||||||
this.gpsLatitude = gpsLatitude;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable Double getGpsLongitude() {
|
public @Nullable Double getGpsLongitude() {
|
||||||
return gpsLongitude;
|
return gpsLongitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGpsLongitude(Double gpsLongitude) {
|
public @Nullable String getLocationUpdated() {
|
||||||
this.gpsLongitude = gpsLongitude;
|
return locationUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Double getExternalTemperature() {
|
||||||
|
return externalTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Double getEstimatedRange() {
|
||||||
|
return estimatedRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlugStatus getPlugStatus() {
|
||||||
|
return plugStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChargingStatus getChargingStatus() {
|
||||||
|
return chargingStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChargingMode getChargingMode() {
|
||||||
|
return chargingMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Integer getChargingRemainingTime() {
|
||||||
|
return chargingRemainingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Double getBatteryAvailableEnergy() {
|
||||||
|
return batteryAvailableEnergy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getHvacTargetTemperature() {
|
||||||
|
return hvacTargetTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHvacTargetTemperature(double hvacTargetTemperature) {
|
||||||
|
this.hvacTargetTemperature = hvacTargetTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisableLocation(boolean disableLocation) {
|
||||||
|
this.disableLocation = disableLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisableBattery(boolean disableBattery) {
|
||||||
|
this.disableBattery = disableBattery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisableCockpit(boolean disableCockpit) {
|
||||||
|
this.disableCockpit = disableCockpit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisableHvac(boolean disableHvac) {
|
||||||
|
this.disableHvac = disableHvac;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the charging mode to a known mode.
|
||||||
|
*
|
||||||
|
* @param mode
|
||||||
|
*/
|
||||||
|
public void setChargeMode(ChargingMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case SCHEDULE_MODE:
|
||||||
|
case ALWAYS_CHARGING:
|
||||||
|
chargingMode = mode;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Nullable JsonObject getAttributes(JsonObject responseJson)
|
private @Nullable JsonObject getAttributes(JsonObject responseJson)
|
||||||
@ -211,4 +311,44 @@ public class Car {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PlugStatus mapPlugStatus(final String apiPlugState) {
|
||||||
|
// https://github.com/hacf-fr/renault-api/blob/main/src/renault_api/kamereon/enums.py
|
||||||
|
switch (apiPlugState) {
|
||||||
|
case "0":
|
||||||
|
return PlugStatus.UNPLUGGED;
|
||||||
|
case "1":
|
||||||
|
return PlugStatus.PLUGGED;
|
||||||
|
case "-1":
|
||||||
|
return PlugStatus.PLUG_ERROR;
|
||||||
|
case "-2147483648":
|
||||||
|
return PlugStatus.PLUG_UNKNOWN;
|
||||||
|
default:
|
||||||
|
return PlugStatus.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChargingStatus mapChargingStatus(final String apiChargeState) {
|
||||||
|
// https://github.com/hacf-fr/renault-api/blob/main/src/renault_api/kamereon/enums.py
|
||||||
|
switch (apiChargeState) {
|
||||||
|
case "0.0":
|
||||||
|
return ChargingStatus.NOT_IN_CHARGE;
|
||||||
|
case "0.1":
|
||||||
|
return ChargingStatus.WAITING_FOR_A_PLANNED_CHARGE;
|
||||||
|
case "0.2":
|
||||||
|
return ChargingStatus.CHARGE_ENDED;
|
||||||
|
case "0.3":
|
||||||
|
return ChargingStatus.WAITING_FOR_CURRENT_CHARGE;
|
||||||
|
case "0.4":
|
||||||
|
return ChargingStatus.ENERGY_FLAP_OPENED;
|
||||||
|
case "1.0":
|
||||||
|
return ChargingStatus.CHARGE_IN_PROGRESS;
|
||||||
|
case "-1.0":
|
||||||
|
return ChargingStatus.CHARGE_ERROR;
|
||||||
|
case "-1.1":
|
||||||
|
return ChargingStatus.UNAVAILABLE;
|
||||||
|
default:
|
||||||
|
return ChargingStatus.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,10 +20,12 @@ import org.eclipse.jdt.annotation.Nullable;
|
|||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.util.Fields;
|
import org.eclipse.jetty.util.Fields;
|
||||||
import org.openhab.binding.renault.internal.RenaultConfiguration;
|
import org.openhab.binding.renault.internal.RenaultConfiguration;
|
||||||
|
import org.openhab.binding.renault.internal.api.Car.ChargingMode;
|
||||||
import org.openhab.binding.renault.internal.api.exceptions.RenaultException;
|
import org.openhab.binding.renault.internal.api.exceptions.RenaultException;
|
||||||
import org.openhab.binding.renault.internal.api.exceptions.RenaultForbiddenException;
|
import org.openhab.binding.renault.internal.api.exceptions.RenaultForbiddenException;
|
||||||
import org.openhab.binding.renault.internal.api.exceptions.RenaultNotImplementedException;
|
import org.openhab.binding.renault.internal.api.exceptions.RenaultNotImplementedException;
|
||||||
@ -46,6 +48,9 @@ import com.google.gson.JsonParser;
|
|||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class MyRenaultHttpSession {
|
public class MyRenaultHttpSession {
|
||||||
|
|
||||||
|
private static final String CHARGING_MODE_SCHEDULE = "schedule_mode";
|
||||||
|
private static final String CHARGING_MODE_ALWAYS = "always_charging";
|
||||||
|
|
||||||
private RenaultConfiguration config;
|
private RenaultConfiguration config;
|
||||||
private HttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
private Constants constants;
|
private Constants constants;
|
||||||
@ -98,6 +103,10 @@ public class MyRenaultHttpSession {
|
|||||||
} catch (JsonParseException | ClassCastException | IllegalStateException e) {
|
} catch (JsonParseException | ClassCastException | IllegalStateException e) {
|
||||||
throw new RenaultException("Login Error: cookie value not found in JSON response");
|
throw new RenaultException("Login Error: cookie value not found in JSON response");
|
||||||
}
|
}
|
||||||
|
if (cookieValue == null) {
|
||||||
|
logger.warn("Login Error: cookie value not found! Response: [{}] {}\n{}", response.getStatus(),
|
||||||
|
response.getReason(), response.getContentAsString());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Response: [{}] {}\n{}", response.getStatus(), response.getReason(),
|
logger.warn("Response: [{}] {}\n{}", response.getStatus(), response.getReason(),
|
||||||
response.getContentAsString());
|
response.getContentAsString());
|
||||||
@ -224,6 +233,75 @@ public class MyRenaultHttpSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void actionHvacOn(double hvacTargetTemperature)
|
||||||
|
throws RenaultForbiddenException, RenaultNotImplementedException {
|
||||||
|
Request request = httpClient
|
||||||
|
.newRequest(this.constants.getKamereonRootUrl() + "/commerce/v1/accounts/" + kamereonaccountId
|
||||||
|
+ "/kamereon/kca/car-adapter/v1/cars/" + config.vin + "/actions/hvac-start?country="
|
||||||
|
+ getCountry(config))
|
||||||
|
.method(HttpMethod.POST).header("Content-type", "application/vnd.api+json")
|
||||||
|
.header("apikey", this.constants.getKamereonApiKey())
|
||||||
|
.header("x-kamereon-authorization", "Bearer " + kamereonToken).header("x-gigya-id_token", jwt);
|
||||||
|
request.content(new StringContentProvider(
|
||||||
|
"{\"data\":{\"type\":\"HvacStart\",\"attributes\":{\"action\":\"start\",\"targetTemperature\":\""
|
||||||
|
+ hvacTargetTemperature + "\"}}}",
|
||||||
|
"utf-8"));
|
||||||
|
try {
|
||||||
|
ContentResponse response = request.send();
|
||||||
|
logger.debug("Kamereon Response HVAC ON: {}", response.getContentAsString());
|
||||||
|
if (HttpStatus.OK_200 != response.getStatus()) {
|
||||||
|
logger.warn("Kamereon Response: [{}] {} {}", response.getStatus(), response.getReason(),
|
||||||
|
response.getContentAsString());
|
||||||
|
if (HttpStatus.FORBIDDEN_403 == response.getStatus()) {
|
||||||
|
throw new RenaultForbiddenException(
|
||||||
|
"Kamereon Response Forbidden! Ensure the car is paired in your MyRenault App.");
|
||||||
|
} else if (HttpStatus.NOT_IMPLEMENTED_501 == response.getStatus()) {
|
||||||
|
throw new RenaultNotImplementedException(
|
||||||
|
"Kamereon Service Not Implemented: [" + response.getStatus() + "] " + response.getReason());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.warn("Kamereon Request: {} threw exception: {} ", request.getURI().toString(), e.getMessage());
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (JsonParseException | TimeoutException | ExecutionException e) {
|
||||||
|
logger.warn("Kamereon Request: {} threw exception: {} ", request.getURI().toString(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void actionChargeMode(ChargingMode mode) throws RenaultForbiddenException, RenaultNotImplementedException {
|
||||||
|
Request request = httpClient
|
||||||
|
.newRequest(this.constants.getKamereonRootUrl() + "/commerce/v1/accounts/" + kamereonaccountId
|
||||||
|
+ "/kamereon/kca/car-adapter/v1/cars/" + config.vin + "/actions/charge-mode?country="
|
||||||
|
+ getCountry(config))
|
||||||
|
.method(HttpMethod.POST).header("Content-type", "application/vnd.api+json")
|
||||||
|
.header("apikey", this.constants.getKamereonApiKey())
|
||||||
|
.header("x-kamereon-authorization", "Bearer " + kamereonToken).header("x-gigya-id_token", jwt);
|
||||||
|
|
||||||
|
final String apiMode = ChargingMode.SCHEDULE_MODE.equals(mode) ? CHARGING_MODE_SCHEDULE : CHARGING_MODE_ALWAYS;
|
||||||
|
request.content(new StringContentProvider(
|
||||||
|
"{\"data\":{\"type\":\"ChargeMode\",\"attributes\":{\"action\":\"" + apiMode + "\"}}}", "utf-8"));
|
||||||
|
try {
|
||||||
|
ContentResponse response = request.send();
|
||||||
|
logger.debug("Kamereon Response set ChargeMode: {}", response.getContentAsString());
|
||||||
|
if (HttpStatus.OK_200 != response.getStatus()) {
|
||||||
|
logger.warn("Kamereon Response: [{}] {} {}", response.getStatus(), response.getReason(),
|
||||||
|
response.getContentAsString());
|
||||||
|
if (HttpStatus.FORBIDDEN_403 == response.getStatus()) {
|
||||||
|
throw new RenaultForbiddenException(
|
||||||
|
"Kamereon Response Forbidden! Ensure the car is paired in your MyRenault App.");
|
||||||
|
} else if (HttpStatus.NOT_IMPLEMENTED_501 == response.getStatus()) {
|
||||||
|
throw new RenaultNotImplementedException(
|
||||||
|
"Kamereon Service Not Implemented: [" + response.getStatus() + "] " + response.getReason());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.warn("Kamereon Request: {} threw exception: {} ", request.getURI().toString(), e.getMessage());
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (JsonParseException | TimeoutException | ExecutionException e) {
|
||||||
|
logger.warn("Kamereon Request: {} threw exception: {} ", request.getURI().toString(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private @Nullable JsonObject getKamereonResponse(String path)
|
private @Nullable JsonObject getKamereonResponse(String path)
|
||||||
throws RenaultForbiddenException, RenaultUpdateException, RenaultNotImplementedException {
|
throws RenaultForbiddenException, RenaultUpdateException, RenaultNotImplementedException {
|
||||||
Request request = httpClient.newRequest(this.constants.getKamereonRootUrl() + path).method(HttpMethod.GET)
|
Request request = httpClient.newRequest(this.constants.getKamereonRootUrl() + path).method(HttpMethod.GET)
|
||||||
@ -248,7 +326,10 @@ public class MyRenaultHttpSession {
|
|||||||
"Kamereon Response Failed! Error: [" + response.getStatus() + "] " + response.getReason());
|
"Kamereon Response Failed! Error: [" + response.getStatus() + "] " + response.getReason());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (JsonParseException | InterruptedException | TimeoutException | ExecutionException e) {
|
} catch (InterruptedException e) {
|
||||||
|
logger.warn("Kamereon Request: {} threw exception: {} ", request.getURI().toString(), e.getMessage());
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (JsonParseException | TimeoutException | ExecutionException e) {
|
||||||
logger.warn("Kamereon Request: {} threw exception: {} ", request.getURI().toString(), e.getMessage());
|
logger.warn("Kamereon Request: {} threw exception: {} ", request.getURI().toString(), e.getMessage());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -15,32 +15,44 @@ package org.openhab.binding.renault.internal.handler;
|
|||||||
import static org.openhab.binding.renault.internal.RenaultBindingConstants.*;
|
import static org.openhab.binding.renault.internal.RenaultBindingConstants.*;
|
||||||
import static org.openhab.core.library.unit.MetricPrefix.KILO;
|
import static org.openhab.core.library.unit.MetricPrefix.KILO;
|
||||||
import static org.openhab.core.library.unit.SIUnits.METRE;
|
import static org.openhab.core.library.unit.SIUnits.METRE;
|
||||||
|
import static org.openhab.core.library.unit.Units.KILOWATT_HOUR;
|
||||||
|
import static org.openhab.core.library.unit.Units.MINUTE;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import javax.measure.quantity.Energy;
|
||||||
import javax.measure.quantity.Length;
|
import javax.measure.quantity.Length;
|
||||||
|
import javax.measure.quantity.Temperature;
|
||||||
|
import javax.measure.quantity.Time;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.openhab.binding.renault.internal.RenaultBindingConstants;
|
||||||
import org.openhab.binding.renault.internal.RenaultConfiguration;
|
import org.openhab.binding.renault.internal.RenaultConfiguration;
|
||||||
import org.openhab.binding.renault.internal.api.Car;
|
import org.openhab.binding.renault.internal.api.Car;
|
||||||
|
import org.openhab.binding.renault.internal.api.Car.ChargingMode;
|
||||||
import org.openhab.binding.renault.internal.api.MyRenaultHttpSession;
|
import org.openhab.binding.renault.internal.api.MyRenaultHttpSession;
|
||||||
|
import org.openhab.binding.renault.internal.api.exceptions.RenaultException;
|
||||||
import org.openhab.binding.renault.internal.api.exceptions.RenaultForbiddenException;
|
import org.openhab.binding.renault.internal.api.exceptions.RenaultForbiddenException;
|
||||||
import org.openhab.binding.renault.internal.api.exceptions.RenaultNotImplementedException;
|
import org.openhab.binding.renault.internal.api.exceptions.RenaultNotImplementedException;
|
||||||
import org.openhab.binding.renault.internal.api.exceptions.RenaultUpdateException;
|
import org.openhab.binding.renault.internal.api.exceptions.RenaultUpdateException;
|
||||||
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
|
||||||
import org.openhab.core.library.types.PointType;
|
import org.openhab.core.library.types.PointType;
|
||||||
import org.openhab.core.library.types.QuantityType;
|
import org.openhab.core.library.types.QuantityType;
|
||||||
import org.openhab.core.library.types.StringType;
|
import org.openhab.core.library.types.StringType;
|
||||||
|
import org.openhab.core.library.unit.SIUnits;
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
import org.openhab.core.thing.ThingStatusDetail;
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.RefreshType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -69,11 +81,6 @@ public class RenaultHandler extends BaseThingHandler {
|
|||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
|
||||||
// This binding only polls status data automatically.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
// reset the car on initialize
|
// reset the car on initialize
|
||||||
@ -104,6 +111,9 @@ public class RenaultHandler extends BaseThingHandler {
|
|||||||
}
|
}
|
||||||
updateStatus(ThingStatus.UNKNOWN);
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
|
|
||||||
|
updateState(CHANNEL_HVAC_TARGET_TEMPERATURE,
|
||||||
|
new QuantityType<Temperature>(car.getHvacTargetTemperature(), SIUnits.CELSIUS));
|
||||||
|
|
||||||
// Background initialization:
|
// Background initialization:
|
||||||
ScheduledFuture<?> job = pollingJob;
|
ScheduledFuture<?> job = pollingJob;
|
||||||
if (job == null || job.isCancelled()) {
|
if (job == null || job.isCancelled()) {
|
||||||
@ -111,6 +121,86 @@ public class RenaultHandler extends BaseThingHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
switch (channelUID.getId()) {
|
||||||
|
case RenaultBindingConstants.CHANNEL_HVAC_TARGET_TEMPERATURE:
|
||||||
|
if (!car.isDisableHvac()) {
|
||||||
|
if (command instanceof RefreshType) {
|
||||||
|
updateState(CHANNEL_HVAC_TARGET_TEMPERATURE,
|
||||||
|
new QuantityType<Temperature>(car.getHvacTargetTemperature(), SIUnits.CELSIUS));
|
||||||
|
} else if (command instanceof DecimalType) {
|
||||||
|
car.setHvacTargetTemperature(((DecimalType) command).doubleValue());
|
||||||
|
updateState(CHANNEL_HVAC_TARGET_TEMPERATURE,
|
||||||
|
new QuantityType<Temperature>(car.getHvacTargetTemperature(), SIUnits.CELSIUS));
|
||||||
|
} else if (command instanceof QuantityType) {
|
||||||
|
@Nullable
|
||||||
|
QuantityType<Temperature> celsius = ((QuantityType<Temperature>) command)
|
||||||
|
.toUnit(SIUnits.CELSIUS);
|
||||||
|
if (celsius != null) {
|
||||||
|
car.setHvacTargetTemperature(celsius.doubleValue());
|
||||||
|
}
|
||||||
|
updateState(CHANNEL_HVAC_TARGET_TEMPERATURE,
|
||||||
|
new QuantityType<Temperature>(car.getHvacTargetTemperature(), SIUnits.CELSIUS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RenaultBindingConstants.CHANNEL_HVAC_STATUS:
|
||||||
|
// We can only trigger pre-conditioning of the car.
|
||||||
|
if (command instanceof StringType && command.toString().equals(Car.HVAC_STATUS_ON)
|
||||||
|
&& !car.isDisableHvac()) {
|
||||||
|
final MyRenaultHttpSession httpSession = new MyRenaultHttpSession(this.config, httpClient);
|
||||||
|
try {
|
||||||
|
updateState(CHANNEL_HVAC_STATUS, new StringType(Car.HVAC_STATUS_PENDING));
|
||||||
|
car.resetHVACStatus();
|
||||||
|
httpSession.initSesssion(car);
|
||||||
|
httpSession.actionHvacOn(car.getHvacTargetTemperature());
|
||||||
|
if (pollingJob != null) {
|
||||||
|
pollingJob.cancel(true);
|
||||||
|
}
|
||||||
|
pollingJob = scheduler.scheduleWithFixedDelay(this::getStatus, config.updateDelay,
|
||||||
|
config.refreshInterval * 60, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.warn("Error My Renault Http Session.", e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (RenaultException | RenaultForbiddenException | RenaultUpdateException
|
||||||
|
| RenaultNotImplementedException | ExecutionException | TimeoutException e) {
|
||||||
|
logger.warn("Error My Renault Http Session.", e);
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RenaultBindingConstants.CHANNEL_CHARGING_MODE:
|
||||||
|
if (command instanceof StringType) {
|
||||||
|
try {
|
||||||
|
ChargingMode newMode = ChargingMode.valueOf(command.toString());
|
||||||
|
if (!ChargingMode.UNKNOWN.equals(newMode)) {
|
||||||
|
MyRenaultHttpSession httpSession = new MyRenaultHttpSession(this.config, httpClient);
|
||||||
|
try {
|
||||||
|
httpSession.initSesssion(car);
|
||||||
|
httpSession.actionChargeMode(newMode);
|
||||||
|
car.setChargeMode(newMode);
|
||||||
|
updateState(CHANNEL_CHARGING_MODE, new StringType(newMode.toString()));
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.warn("Error My Renault Http Session.", e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (RenaultException | RenaultForbiddenException | RenaultUpdateException
|
||||||
|
| RenaultNotImplementedException | ExecutionException | TimeoutException e) {
|
||||||
|
logger.warn("Error My Renault Http Session.", e);
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.warn("Invalid ChargingMode {}.", command.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
ScheduledFuture<?> job = pollingJob;
|
ScheduledFuture<?> job = pollingJob;
|
||||||
@ -126,8 +216,10 @@ public class RenaultHandler extends BaseThingHandler {
|
|||||||
try {
|
try {
|
||||||
httpSession.initSesssion(car);
|
httpSession.initSesssion(car);
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.warn("Error My Renault Http Session.", e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
httpSession = null;
|
|
||||||
logger.warn("Error My Renault Http Session.", e);
|
logger.warn("Error My Renault Http Session.", e);
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||||
}
|
}
|
||||||
@ -148,8 +240,17 @@ public class RenaultHandler extends BaseThingHandler {
|
|||||||
try {
|
try {
|
||||||
httpSession.getHvacStatus(car);
|
httpSession.getHvacStatus(car);
|
||||||
Boolean hvacstatus = car.getHvacstatus();
|
Boolean hvacstatus = car.getHvacstatus();
|
||||||
if (hvacstatus != null) {
|
if (hvacstatus == null) {
|
||||||
updateState(CHANNEL_HVAC_STATUS, OnOffType.from(hvacstatus.booleanValue()));
|
updateState(CHANNEL_HVAC_STATUS, new StringType(Car.HVAC_STATUS_PENDING));
|
||||||
|
} else if (hvacstatus.booleanValue()) {
|
||||||
|
updateState(CHANNEL_HVAC_STATUS, new StringType(Car.HVAC_STATUS_ON));
|
||||||
|
} else {
|
||||||
|
updateState(CHANNEL_HVAC_STATUS, new StringType(Car.HVAC_STATUS_OFF));
|
||||||
|
}
|
||||||
|
Double externalTemperature = car.getExternalTemperature();
|
||||||
|
if (externalTemperature != null) {
|
||||||
|
updateState(CHANNEL_EXTERNAL_TEMPERATURE,
|
||||||
|
new QuantityType<Temperature>(externalTemperature.doubleValue(), SIUnits.CELSIUS));
|
||||||
}
|
}
|
||||||
} catch (RenaultNotImplementedException e) {
|
} catch (RenaultNotImplementedException e) {
|
||||||
car.setDisableHvac(true);
|
car.setDisableHvac(true);
|
||||||
@ -168,9 +269,13 @@ public class RenaultHandler extends BaseThingHandler {
|
|||||||
updateState(CHANNEL_LOCATION, new PointType(new DecimalType(latitude.doubleValue()),
|
updateState(CHANNEL_LOCATION, new PointType(new DecimalType(latitude.doubleValue()),
|
||||||
new DecimalType(longitude.doubleValue())));
|
new DecimalType(longitude.doubleValue())));
|
||||||
}
|
}
|
||||||
|
String locationUpdated = car.getLocationUpdated();
|
||||||
|
if (locationUpdated != null) {
|
||||||
|
updateState(CHANNEL_LOCATION_UPDATED, new DateTimeType(locationUpdated));
|
||||||
|
}
|
||||||
} catch (RenaultNotImplementedException e) {
|
} catch (RenaultNotImplementedException e) {
|
||||||
car.setDisableLocation(true);
|
car.setDisableLocation(true);
|
||||||
} catch (RenaultForbiddenException | RenaultUpdateException e) {
|
} catch (IllegalArgumentException | RenaultForbiddenException | RenaultUpdateException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,10 +299,27 @@ public class RenaultHandler extends BaseThingHandler {
|
|||||||
if (!car.isDisableBattery()) {
|
if (!car.isDisableBattery()) {
|
||||||
try {
|
try {
|
||||||
httpSession.getBatteryStatus(car);
|
httpSession.getBatteryStatus(car);
|
||||||
|
updateState(CHANNEL_PLUG_STATUS, new StringType(car.getPlugStatus().name()));
|
||||||
|
updateState(CHANNEL_CHARGING_STATUS, new StringType(car.getChargingStatus().name()));
|
||||||
Double batteryLevel = car.getBatteryLevel();
|
Double batteryLevel = car.getBatteryLevel();
|
||||||
if (batteryLevel != null) {
|
if (batteryLevel != null) {
|
||||||
updateState(CHANNEL_BATTERY_LEVEL, new DecimalType(batteryLevel.doubleValue()));
|
updateState(CHANNEL_BATTERY_LEVEL, new DecimalType(batteryLevel.doubleValue()));
|
||||||
}
|
}
|
||||||
|
Double estimatedRange = car.getEstimatedRange();
|
||||||
|
if (estimatedRange != null) {
|
||||||
|
updateState(CHANNEL_ESTIMATED_RANGE,
|
||||||
|
new QuantityType<Length>(estimatedRange.doubleValue(), KILO(METRE)));
|
||||||
|
}
|
||||||
|
Double batteryAvailableEnergy = car.getBatteryAvailableEnergy();
|
||||||
|
if (batteryAvailableEnergy != null) {
|
||||||
|
updateState(CHANNEL_BATTERY_AVAILABLE_ENERGY,
|
||||||
|
new QuantityType<Energy>(batteryAvailableEnergy.doubleValue(), KILOWATT_HOUR));
|
||||||
|
}
|
||||||
|
Integer chargingRemainingTime = car.getChargingRemainingTime();
|
||||||
|
if (chargingRemainingTime != null) {
|
||||||
|
updateState(CHANNEL_CHARGING_REMAINING_TIME,
|
||||||
|
new QuantityType<Time>(chargingRemainingTime.doubleValue(), MINUTE));
|
||||||
|
}
|
||||||
} catch (RenaultNotImplementedException e) {
|
} catch (RenaultNotImplementedException e) {
|
||||||
car.setDisableBattery(true);
|
car.setDisableBattery(true);
|
||||||
} catch (RenaultForbiddenException | RenaultUpdateException e) {
|
} catch (RenaultForbiddenException | RenaultUpdateException e) {
|
||||||
|
|||||||
@ -45,13 +45,50 @@ thing-type.config.renault.car.myRenaultPassword.label = MyRenault Password
|
|||||||
thing-type.config.renault.car.myRenaultUsername.label = MyRenault Username
|
thing-type.config.renault.car.myRenaultUsername.label = MyRenault Username
|
||||||
thing-type.config.renault.car.refreshInterval.label = Refresh Interval
|
thing-type.config.renault.car.refreshInterval.label = Refresh Interval
|
||||||
thing-type.config.renault.car.refreshInterval.description = Interval the car is polled in minutes.
|
thing-type.config.renault.car.refreshInterval.description = Interval the car is polled in minutes.
|
||||||
|
thing-type.config.renault.car.updateDelay.label = Update Delay
|
||||||
|
thing-type.config.renault.car.updateDelay.description = How long to wait for commands to reach car and update to server in seconds.
|
||||||
thing-type.config.renault.car.vin.label = VIN
|
thing-type.config.renault.car.vin.label = VIN
|
||||||
thing-type.config.renault.car.vin.description = Vehicle Identification Number
|
thing-type.config.renault.car.vin.description = Vehicle Identification Number
|
||||||
|
|
||||||
# channel types
|
# channel types
|
||||||
|
|
||||||
channel-type.renault.hvacstatus.label = HVAC Status
|
channel-type.renault.batteryavailableenergy.label = Battery Energy Available
|
||||||
|
channel-type.renault.chargingmode.label = Charging Mode
|
||||||
|
channel-type.renault.chargingmode.state.option.UNKNOWN = Unknown
|
||||||
|
channel-type.renault.chargingmode.state.option.SCHEDULE_MODE = Schedule mode
|
||||||
|
channel-type.renault.chargingmode.state.option.ALWAYS_CHARGING = Instant charge
|
||||||
|
channel-type.renault.chargingremainingtime.label = Charging Time Remaining
|
||||||
|
channel-type.renault.chargingstatus.label = Charging Status
|
||||||
|
channel-type.renault.chargingstatus.state.option.NOT_IN_CHARGE = Not charging
|
||||||
|
channel-type.renault.chargingstatus.state.option.WAITING_FOR_A_PLANNED_CHARGE = Waiting for schedule
|
||||||
|
channel-type.renault.chargingstatus.state.option.CHARGE_ENDED = Charge ended
|
||||||
|
channel-type.renault.chargingstatus.state.option.WAITING_FOR_CURRENT_CHARGE = Waiting for charge
|
||||||
|
channel-type.renault.chargingstatus.state.option.ENERGY_FLAP_OPENED = Plug flap opened
|
||||||
|
channel-type.renault.chargingstatus.state.option.CHARGE_IN_PROGRESS = Charge in progress
|
||||||
|
channel-type.renault.chargingstatus.state.option.CHARGE_ERROR = Charge error
|
||||||
|
channel-type.renault.chargingstatus.state.option.UNAVAILABLE = Unavailable
|
||||||
|
channel-type.renault.chargingstatus.state.option.UNKNOWN = Unknown
|
||||||
|
channel-type.renault.estimatedrange.label = Estimated Range
|
||||||
|
channel-type.renault.estimatedrange.description = Estimated range of the car.
|
||||||
|
channel-type.renault.externaltemperature.label = External Temperature
|
||||||
|
channel-type.renault.externaltemperature.description = Temperature outside of the car
|
||||||
|
channel-type.renault.hvacstatus.label = HVAC Status (ON | OFF | PENDING)
|
||||||
|
channel-type.renault.hvacstatus.state.option.ON = On
|
||||||
|
channel-type.renault.hvacstatus.state.option.PENDING = Pending
|
||||||
|
channel-type.renault.hvacstatus.state.option.OFF = Off
|
||||||
|
channel-type.renault.hvactargettemperature.label = HVAC Target Temperature
|
||||||
|
channel-type.renault.hvactargettemperature.description = HVAC target temperature (19 to 21)
|
||||||
channel-type.renault.image.label = Image URL
|
channel-type.renault.image.label = Image URL
|
||||||
channel-type.renault.image.description = Image URL of MyRenault
|
channel-type.renault.image.description = Image URL of MyRenault
|
||||||
|
channel-type.renault.locationupdated.label = Location Update
|
||||||
|
channel-type.renault.locationupdated.description = Timestamp of the last location update
|
||||||
|
channel-type.renault.locationupdated.state.pattern = %1$tH:%1$tM %1$td.%1$tm.%1$tY
|
||||||
channel-type.renault.odometer.label = Odometer
|
channel-type.renault.odometer.label = Odometer
|
||||||
channel-type.renault.odometer.description = Total distance travelled
|
channel-type.renault.odometer.description = Total distance travelled
|
||||||
|
channel-type.renault.plugstatus.label = Plug Status
|
||||||
|
channel-type.renault.plugstatus.description = Status of charging plug.
|
||||||
|
channel-type.renault.plugstatus.state.option.UNPLUGGED = Unplugged
|
||||||
|
channel-type.renault.plugstatus.state.option.PLUGGED = Plugged
|
||||||
|
channel-type.renault.plugstatus.state.option.PLUG_ERROR = Plug error
|
||||||
|
channel-type.renault.plugstatus.state.option.PLUG_UNKNOWN = Plug unknown
|
||||||
|
channel-type.renault.plugstatus.state.option.UNKNOWN = Unknown
|
||||||
|
|||||||
@ -11,10 +11,19 @@
|
|||||||
|
|
||||||
<channels>
|
<channels>
|
||||||
<channel id="batterylevel" typeId="system.battery-level"/>
|
<channel id="batterylevel" typeId="system.battery-level"/>
|
||||||
|
<channel id="batteryavailableenergy" typeId="batteryavailableenergy"/>
|
||||||
|
<channel id="plugstatus" typeId="plugstatus"/>
|
||||||
|
<channel id="chargingstatus" typeId="chargingstatus"/>
|
||||||
|
<channel id="chargingmode" typeId="chargingmode"/>
|
||||||
|
<channel id="chargingremainingtime" typeId="chargingremainingtime"/>
|
||||||
|
<channel id="estimatedrange" typeId="estimatedrange"/>
|
||||||
|
<channel id="odometer" typeId="odometer"/>
|
||||||
<channel id="hvacstatus" typeId="hvacstatus"/>
|
<channel id="hvacstatus" typeId="hvacstatus"/>
|
||||||
|
<channel id="hvactargettemperature" typeId="hvactargettemperature"/>
|
||||||
|
<channel id="externaltemperature" typeId="externaltemperature"/>
|
||||||
<channel id="image" typeId="image"/>
|
<channel id="image" typeId="image"/>
|
||||||
<channel id="location" typeId="system.location"/>
|
<channel id="location" typeId="system.location"/>
|
||||||
<channel id="odometer" typeId="odometer"/>
|
<channel id="locationupdated" typeId="locationupdated"/>
|
||||||
</channels>
|
</channels>
|
||||||
<config-description>
|
<config-description>
|
||||||
|
|
||||||
@ -69,20 +78,72 @@
|
|||||||
<description>Interval the car is polled in minutes.</description>
|
<description>Interval the car is polled in minutes.</description>
|
||||||
<default>10</default>
|
<default>10</default>
|
||||||
</parameter>
|
</parameter>
|
||||||
|
<parameter name="updateDelay" type="integer" unit="s" min="5" max="120">
|
||||||
|
<label>Update Delay</label>
|
||||||
|
<description>How long to wait for commands to reach car and update to server in seconds.</description>
|
||||||
|
<default>30</default>
|
||||||
|
</parameter>
|
||||||
</config-description>
|
</config-description>
|
||||||
</thing-type>
|
</thing-type>
|
||||||
|
|
||||||
<!-- Sample Channel Type -->
|
<channel-type id="batteryavailableenergy">
|
||||||
<channel-type id="hvacstatus">
|
<item-type>Number:Energy</item-type>
|
||||||
<item-type>Switch</item-type>
|
<label>Battery Energy Available</label>
|
||||||
<label>HVAC Status</label>
|
<state pattern="%.1f %unit%" readOnly="true"></state>
|
||||||
<state readOnly="true"></state>
|
|
||||||
</channel-type>
|
</channel-type>
|
||||||
<channel-type id="image">
|
<channel-type id="plugstatus">
|
||||||
<item-type>String</item-type>
|
<item-type>String</item-type>
|
||||||
<label>Image URL</label>
|
<label>Plug Status</label>
|
||||||
<description>Image URL of MyRenault</description>
|
<description>Status of charging plug.</description>
|
||||||
<state readOnly="true"></state>
|
<state readOnly="true">
|
||||||
|
<options>
|
||||||
|
<option value="UNPLUGGED">Unplugged</option>
|
||||||
|
<option value="PLUGGED">Plugged</option>
|
||||||
|
<option value="PLUG_ERROR">Plug error</option>
|
||||||
|
<option value="PLUG_UNKNOWN">Plug unknown</option>
|
||||||
|
<option value="UNKNOWN">Unknown</option>
|
||||||
|
</options>
|
||||||
|
</state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="chargingstatus">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Charging Status</label>
|
||||||
|
<state readOnly="true">
|
||||||
|
<options>
|
||||||
|
<option value="NOT_IN_CHARGE">Not charging</option>
|
||||||
|
<option value="WAITING_FOR_A_PLANNED_CHARGE">Waiting for schedule</option>
|
||||||
|
<option value="CHARGE_ENDED">Charge ended</option>
|
||||||
|
<option value="WAITING_FOR_CURRENT_CHARGE">Waiting for charge</option>
|
||||||
|
<option value="ENERGY_FLAP_OPENED">Plug flap opened</option>
|
||||||
|
<option value="CHARGE_IN_PROGRESS">Charge in progress</option>
|
||||||
|
<option value="CHARGE_ERROR">Charge error</option>
|
||||||
|
<option value="UNAVAILABLE">Unavailable</option>
|
||||||
|
<option value="UNKNOWN">Unknown</option>
|
||||||
|
</options>
|
||||||
|
</state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="chargingmode">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Charging Mode</label>
|
||||||
|
<state readOnly="false">
|
||||||
|
<options>
|
||||||
|
<option value="UNKNOWN">Unknown</option>
|
||||||
|
<option value="SCHEDULE_MODE">Schedule mode</option>
|
||||||
|
<option value="ALWAYS_CHARGING">Instant charge</option>
|
||||||
|
</options>
|
||||||
|
</state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="chargingremainingtime">
|
||||||
|
<item-type>Number:Time</item-type>
|
||||||
|
<label>Charging Time Remaining</label>
|
||||||
|
<category>Time</category>
|
||||||
|
<state pattern="%d %unit%" readOnly="true"></state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="estimatedrange">
|
||||||
|
<item-type>Number:Length</item-type>
|
||||||
|
<label>Estimated Range</label>
|
||||||
|
<description>Estimated range of the car.</description>
|
||||||
|
<state pattern="%.1f %unit%" readOnly="true"></state>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
<channel-type id="odometer">
|
<channel-type id="odometer">
|
||||||
<item-type>Number:Length</item-type>
|
<item-type>Number:Length</item-type>
|
||||||
@ -90,5 +151,42 @@
|
|||||||
<description>Total distance travelled</description>
|
<description>Total distance travelled</description>
|
||||||
<state pattern="%.1f %unit%" readOnly="true"></state>
|
<state pattern="%.1f %unit%" readOnly="true"></state>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
<channel-type id="hvacstatus">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>HVAC Status (ON | OFF | PENDING)</label>
|
||||||
|
<state readOnly="false">
|
||||||
|
<options>
|
||||||
|
<option value="ON">On</option>
|
||||||
|
<option value="PENDING">Pending</option>
|
||||||
|
<option value="OFF">Off</option>
|
||||||
|
</options>
|
||||||
|
</state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="hvactargettemperature" advanced="true">
|
||||||
|
<item-type>Number:Temperature</item-type>
|
||||||
|
<label>HVAC Target Temperature</label>
|
||||||
|
<description>HVAC target temperature (19 to 21)</description>
|
||||||
|
<category>Temperature</category>
|
||||||
|
<state pattern="%.1f %unit%"></state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="externaltemperature" advanced="true">
|
||||||
|
<item-type>Number:Temperature</item-type>
|
||||||
|
<label>External Temperature</label>
|
||||||
|
<description>Temperature outside of the car</description>
|
||||||
|
<category>Temperature</category>
|
||||||
|
<state pattern="%.1f %unit%" readOnly="true"></state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="image">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Image URL</label>
|
||||||
|
<description>Image URL of MyRenault</description>
|
||||||
|
<state readOnly="true"></state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="locationupdated">
|
||||||
|
<item-type>DateTime</item-type>
|
||||||
|
<label>Location Update</label>
|
||||||
|
<description>Timestamp of the last location update</description>
|
||||||
|
<state pattern="%1$tH:%1$tM %1$td.%1$tm.%1$tY" readOnly="true"></state>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
</thing:thing-descriptions>
|
</thing:thing-descriptions>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user