[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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
No discovery
|
||||
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
You require your MyRenault credential, locale and VIN for your MyRenault registered car.
|
||||
|
||||
| Parameter | Description | Required |
|
||||
|-------------------|----------------------------------------|----------|
|
||||
| myRenaultUsername | MyRenault Username. | yes |
|
||||
| myRenaultPassword | MyRenault Password. | yes |
|
||||
| locale | MyRenault Location (language_country). | yes |
|
||||
| vin | Vehicle Identification Number. | yes |
|
||||
| refreshInterval | Interval the car is polled in minutes. | no |
|
||||
| Parameter | Description | Required |
|
||||
|-------------------|----------------------------------------------------------------------------|----------|
|
||||
| myRenaultUsername | MyRenault Username. | yes |
|
||||
| myRenaultPassword | MyRenault Password. | yes |
|
||||
| locale | MyRenault Location (language_country). | yes |
|
||||
| vin | Vehicle Identification Number. | yes |
|
||||
| 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
|
||||
|
||||
Currently all available channels are read only:
|
||||
|
||||
| Channel ID | Type | Description |
|
||||
|--------------|---------------|---------------------------------|
|
||||
| batterylevel | Number | State of the battery in % |
|
||||
| hvacstatus | Switch | HVAC status switch |
|
||||
| image | String | Image URL of MyRenault |
|
||||
| location | Location | The GPS position of the vehicle |
|
||||
| odometer | Number:Length | Total distance travelled |
|
||||
| Channel ID | Type | Description | Read Only |
|
||||
|------------------------|--------------------|-------------------------------------------------|-----------|
|
||||
| batteryavailableEnergy | Number:Energy | Battery Energy Available | Yes |
|
||||
| batterylevel | Number | State of the battery in % | Yes |
|
||||
| chargingmode | String | Charging mode. always_charging or schedule_mode | No |
|
||||
| chargingstatus | String | Charging status | Yes |
|
||||
| chargingremainingtime | Number:Time | Charging time remaining | Yes |
|
||||
| plugstatus | String | Status of charging plug | Yes |
|
||||
| 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");
|
||||
|
||||
// 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_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_TARGET_TEMPERATURE = "hvactargettemperature";
|
||||
public static final String CHANNEL_IMAGE = "image";
|
||||
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_PLUG_STATUS = "plugstatus";
|
||||
}
|
||||
|
||||
@ -27,4 +27,5 @@ public class RenaultConfiguration {
|
||||
public String locale = "";
|
||||
public String vin = "";
|
||||
public int refreshInterval = 10;
|
||||
public int updateDelay = 30;
|
||||
}
|
||||
|
||||
@ -30,6 +30,10 @@ import com.google.gson.JsonObject;
|
||||
@NonNullByDefault
|
||||
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 boolean disableLocation = false;
|
||||
@ -37,29 +41,90 @@ public class Car {
|
||||
private boolean disableCockpit = 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 batteryAvailableEnergy;
|
||||
private @Nullable Integer chargingRemainingTime;
|
||||
private @Nullable Boolean hvacstatus;
|
||||
private @Nullable Double odometer;
|
||||
private @Nullable Double estimatedRange;
|
||||
private @Nullable String imageURL;
|
||||
private @Nullable String locationUpdated;
|
||||
private @Nullable Double gpsLatitude;
|
||||
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) {
|
||||
try {
|
||||
JsonObject attributes = getAttributes(responseJson);
|
||||
if (attributes != null && attributes.get("batteryLevel") != null) {
|
||||
batteryLevel = attributes.get("batteryLevel").getAsDouble();
|
||||
if (attributes != null) {
|
||||
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) {
|
||||
logger.warn("Error {} parsing Battery Status: {}", e.getMessage(), responseJson);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetHVACStatus() {
|
||||
this.hvacstatus = null;
|
||||
}
|
||||
|
||||
public void setHVACStatus(JsonObject responseJson) {
|
||||
try {
|
||||
JsonObject attributes = getAttributes(responseJson);
|
||||
if (attributes != null && attributes.get("hvacStatus") != null) {
|
||||
hvacstatus = attributes.get("hvacStatus").getAsString().equals("on");
|
||||
if (attributes != null) {
|
||||
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) {
|
||||
logger.warn("Error {} parsing HVAC Status: {}", e.getMessage(), responseJson);
|
||||
@ -87,6 +152,9 @@ public class Car {
|
||||
if (attributes.get("gpsLongitude") != null) {
|
||||
gpsLongitude = attributes.get("gpsLongitude").getAsDouble();
|
||||
}
|
||||
if (attributes.get("lastUpdateTime") != null) {
|
||||
locationUpdated = attributes.get("lastUpdateTime").getAsString();
|
||||
}
|
||||
}
|
||||
} catch (IllegalStateException | ClassCastException e) {
|
||||
logger.warn("Error {} parsing Location: {}", e.getMessage(), responseJson);
|
||||
@ -128,80 +196,112 @@ public class Car {
|
||||
return disableLocation;
|
||||
}
|
||||
|
||||
public void setDisableLocation(boolean disableLocation) {
|
||||
this.disableLocation = disableLocation;
|
||||
}
|
||||
|
||||
public boolean isDisableBattery() {
|
||||
return disableBattery;
|
||||
}
|
||||
|
||||
public void setDisableBattery(boolean disableBattery) {
|
||||
this.disableBattery = disableBattery;
|
||||
}
|
||||
|
||||
public boolean isDisableCockpit() {
|
||||
return disableCockpit;
|
||||
}
|
||||
|
||||
public void setDisableCockpit(boolean disableCockpit) {
|
||||
this.disableCockpit = disableCockpit;
|
||||
}
|
||||
|
||||
public boolean isDisableHvac() {
|
||||
return disableHvac;
|
||||
}
|
||||
|
||||
public void setDisableHvac(boolean disableHvac) {
|
||||
this.disableHvac = disableHvac;
|
||||
}
|
||||
|
||||
public @Nullable Double getBatteryLevel() {
|
||||
return batteryLevel;
|
||||
}
|
||||
|
||||
public void setBatteryLevel(Double batteryLevel) {
|
||||
this.batteryLevel = batteryLevel;
|
||||
}
|
||||
|
||||
public @Nullable Boolean getHvacstatus() {
|
||||
return hvacstatus;
|
||||
}
|
||||
|
||||
public void setHvacstatus(Boolean hvacstatus) {
|
||||
this.hvacstatus = hvacstatus;
|
||||
}
|
||||
|
||||
public @Nullable Double getOdometer() {
|
||||
return odometer;
|
||||
}
|
||||
|
||||
public void setOdometer(Double odometer) {
|
||||
this.odometer = odometer;
|
||||
}
|
||||
|
||||
public @Nullable String getImageURL() {
|
||||
return imageURL;
|
||||
}
|
||||
|
||||
public void setImageURL(String imageURL) {
|
||||
this.imageURL = imageURL;
|
||||
}
|
||||
|
||||
public @Nullable Double getGpsLatitude() {
|
||||
return gpsLatitude;
|
||||
}
|
||||
|
||||
public void setGpsLatitude(Double gpsLatitude) {
|
||||
this.gpsLatitude = gpsLatitude;
|
||||
}
|
||||
|
||||
public @Nullable Double getGpsLongitude() {
|
||||
return gpsLongitude;
|
||||
}
|
||||
|
||||
public void setGpsLongitude(Double gpsLongitude) {
|
||||
this.gpsLongitude = gpsLongitude;
|
||||
public @Nullable String getLocationUpdated() {
|
||||
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)
|
||||
@ -211,4 +311,44 @@ public class Car {
|
||||
}
|
||||
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.api.ContentResponse;
|
||||
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.HttpStatus;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
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.RenaultForbiddenException;
|
||||
import org.openhab.binding.renault.internal.api.exceptions.RenaultNotImplementedException;
|
||||
@ -46,6 +48,9 @@ import com.google.gson.JsonParser;
|
||||
@NonNullByDefault
|
||||
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 HttpClient httpClient;
|
||||
private Constants constants;
|
||||
@ -98,6 +103,10 @@ public class MyRenaultHttpSession {
|
||||
} catch (JsonParseException | ClassCastException | IllegalStateException e) {
|
||||
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 {
|
||||
logger.warn("Response: [{}] {}\n{}", response.getStatus(), response.getReason(),
|
||||
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)
|
||||
throws RenaultForbiddenException, RenaultUpdateException, RenaultNotImplementedException {
|
||||
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());
|
||||
}
|
||||
}
|
||||
} 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());
|
||||
}
|
||||
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.core.library.unit.MetricPrefix.KILO;
|
||||
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.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.measure.quantity.Energy;
|
||||
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.Nullable;
|
||||
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.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.exceptions.RenaultException;
|
||||
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.RenaultUpdateException;
|
||||
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.PointType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
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.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -69,11 +81,6 @@ public class RenaultHandler extends BaseThingHandler {
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// This binding only polls status data automatically.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// reset the car on initialize
|
||||
@ -104,6 +111,9 @@ public class RenaultHandler extends BaseThingHandler {
|
||||
}
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
updateState(CHANNEL_HVAC_TARGET_TEMPERATURE,
|
||||
new QuantityType<Temperature>(car.getHvacTargetTemperature(), SIUnits.CELSIUS));
|
||||
|
||||
// Background initialization:
|
||||
ScheduledFuture<?> job = pollingJob;
|
||||
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
|
||||
public void dispose() {
|
||||
ScheduledFuture<?> job = pollingJob;
|
||||
@ -126,8 +216,10 @@ public class RenaultHandler extends BaseThingHandler {
|
||||
try {
|
||||
httpSession.initSesssion(car);
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} catch (InterruptedException e) {
|
||||
logger.warn("Error My Renault Http Session.", e);
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
httpSession = null;
|
||||
logger.warn("Error My Renault Http Session.", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
@ -148,8 +240,17 @@ public class RenaultHandler extends BaseThingHandler {
|
||||
try {
|
||||
httpSession.getHvacStatus(car);
|
||||
Boolean hvacstatus = car.getHvacstatus();
|
||||
if (hvacstatus != null) {
|
||||
updateState(CHANNEL_HVAC_STATUS, OnOffType.from(hvacstatus.booleanValue()));
|
||||
if (hvacstatus == null) {
|
||||
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) {
|
||||
car.setDisableHvac(true);
|
||||
@ -168,9 +269,13 @@ public class RenaultHandler extends BaseThingHandler {
|
||||
updateState(CHANNEL_LOCATION, new PointType(new DecimalType(latitude.doubleValue()),
|
||||
new DecimalType(longitude.doubleValue())));
|
||||
}
|
||||
String locationUpdated = car.getLocationUpdated();
|
||||
if (locationUpdated != null) {
|
||||
updateState(CHANNEL_LOCATION_UPDATED, new DateTimeType(locationUpdated));
|
||||
}
|
||||
} catch (RenaultNotImplementedException e) {
|
||||
car.setDisableLocation(true);
|
||||
} catch (RenaultForbiddenException | RenaultUpdateException e) {
|
||||
} catch (IllegalArgumentException | RenaultForbiddenException | RenaultUpdateException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -194,10 +299,27 @@ public class RenaultHandler extends BaseThingHandler {
|
||||
if (!car.isDisableBattery()) {
|
||||
try {
|
||||
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();
|
||||
if (batteryLevel != null) {
|
||||
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) {
|
||||
car.setDisableBattery(true);
|
||||
} 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.refreshInterval.label = Refresh Interval
|
||||
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.description = Vehicle Identification Number
|
||||
|
||||
# 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.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.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>
|
||||
<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="hvactargettemperature" typeId="hvactargettemperature"/>
|
||||
<channel id="externaltemperature" typeId="externaltemperature"/>
|
||||
<channel id="image" typeId="image"/>
|
||||
<channel id="location" typeId="system.location"/>
|
||||
<channel id="odometer" typeId="odometer"/>
|
||||
<channel id="locationupdated" typeId="locationupdated"/>
|
||||
</channels>
|
||||
<config-description>
|
||||
|
||||
@ -69,20 +78,72 @@
|
||||
<description>Interval the car is polled in minutes.</description>
|
||||
<default>10</default>
|
||||
</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>
|
||||
</thing-type>
|
||||
|
||||
<!-- Sample Channel Type -->
|
||||
<channel-type id="hvacstatus">
|
||||
<item-type>Switch</item-type>
|
||||
<label>HVAC Status</label>
|
||||
<state readOnly="true"></state>
|
||||
<channel-type id="batteryavailableenergy">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Battery Energy Available</label>
|
||||
<state pattern="%.1f %unit%" readOnly="true"></state>
|
||||
</channel-type>
|
||||
<channel-type id="image">
|
||||
<channel-type id="plugstatus">
|
||||
<item-type>String</item-type>
|
||||
<label>Image URL</label>
|
||||
<description>Image URL of MyRenault</description>
|
||||
<state readOnly="true"></state>
|
||||
<label>Plug Status</label>
|
||||
<description>Status of charging plug.</description>
|
||||
<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 id="odometer">
|
||||
<item-type>Number:Length</item-type>
|
||||
@ -90,5 +151,42 @@
|
||||
<description>Total distance travelled</description>
|
||||
<state pattern="%.1f %unit%" readOnly="true"></state>
|
||||
</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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user