Skip to content

Commit

Permalink
timestamp
Browse files Browse the repository at this point in the history
  • Loading branch information
wubbl0rz committed Nov 2, 2022
1 parent 22de8b7 commit 80ceb4a
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 39 deletions.
14 changes: 11 additions & 3 deletions FiatChampAddon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
# CHANGELOG

## 3.0.0 - 2022-11-02
- support for different brands.
- jeep should work out of the box
- initial support for Ram Truck
- support for different brands. (fiat, jeep, ram, dodge)
- fiat works
- jeep should work out of the box
- initial support for Ram Truck
- dodge unknown
- instant state restore after home assistant reboot (retain mqtt state messages)
- battery charge now has % unit
- time to charge now has minutes unit
- added last update (time) sensor
- fixed many sensor icons and add device\_class
- added RefreshBatteryStatus button. its the same as DeepRefresh but with a better name.

## 2.0.16 - 2022-10-22
- make mqtt user and password optional. useful when using external brokjer without authentication.
Expand Down
1 change: 1 addition & 0 deletions FiatChampAddon/FiatClient/AppConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public record AppConfig
public string SupervisorToken { get; set; } = null!;
public FcaBrand Brand { get; set; }
public string HomeAssistantUrl { get; set; } = "http://supervisor/core";
public int StartDelaySeconds { get; set; } = 1;
public bool AutoRefreshLocation { get; set; } = false;
public bool AutoRefreshBattery { get; set; } = false;
public bool EnableDangerousCommands { get; set; } = false;
Expand Down
6 changes: 1 addition & 5 deletions FiatChampAddon/FiatClient/FiatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,6 @@ public Task<Vehicle[]> Fetch()
}
}

public class FiatClientApiEndpoints
{
}

public enum FcaBrand
{
Fiat,
Expand Down Expand Up @@ -319,7 +315,7 @@ public FiatClient(string user, string password, FcaBrand brand = FcaBrand.Fiat)
_user = user;
_password = password;
_brand = brand;

if (_brand == FcaBrand.Ram)
{
_loginApiKey = "3_7YjzjoSb7dYtCP5-D6FhPsCciggJFvM14hNPvXN9OsIiV1ujDqa4fNltDJYnHawO";
Expand Down
32 changes: 27 additions & 5 deletions FiatChampAddon/FiatClient/HomeAssistant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,26 @@ public HaRestApi(string token)
_token = token;
}

public async Task<HaRestApiUnitSystem> GetUnitSystem()
private async Task<JObject> GetConfig()
{
var result = await _url
return await _url
.WithOAuthBearerToken(_token)
.AppendPathSegment("config")
.GetJsonAsync<JObject>();
}

public async Task<string> GetTimeZone()
{
var config = await this.GetConfig();

return config["time_zone"].ToString();
}

public async Task<HaRestApiUnitSystem> GetUnitSystem()
{
var config = await this.GetConfig();

return result["unit_system"].ToObject<HaRestApiUnitSystem>();
return config["unit_system"].ToObject<HaRestApiUnitSystem>();
}

public async Task<IReadOnlyList<HaRestApiZone>> GetZones()
Expand Down Expand Up @@ -182,6 +194,7 @@ public class HaSensor : HaEntity
public string Value { get; set; } = "";
public string Icon { get; set; } = "mdi:eye";
public string Unit { get; set; } = "";
public string DeviceClass { get; set; } = "";

private readonly string _stateTopic;
private readonly string _configTopic;
Expand All @@ -199,6 +212,14 @@ public override async Task PublishState()

public override async Task Announce()
{

var unitOfMeasurementJson =
string.IsNullOrWhiteSpace(this.Unit) ? "" : $"\"unit_of_measurement\":\"{this.Unit}\",";
var deviceClassJson =
string.IsNullOrWhiteSpace(this.DeviceClass) ? "" : $"\"device_class\":\"{this.DeviceClass}\"," ;
var iconJson =
string.IsNullOrWhiteSpace(this.DeviceClass) ? $"\"icon\":\"{this.Icon}\"," : "" ;

await _mqttClient.Pub(_configTopic, $$"""
{
"device":{
Expand All @@ -208,8 +229,9 @@ await _mqttClient.Pub(_configTopic, $$"""
"name":"{{ _haDevice.Name }}",
"sw_version":"{{ _haDevice.Version }}"},
"name":"{{ _name }}",
"unit_of_measurement":"{{ this.Unit }}",
"icon":"{{ this.Icon }}",
{{ unitOfMeasurementJson }}
{{ deviceClassJson }}
{{ iconJson }}
"state_topic":"{{ _stateTopic }}",
"unique_id":"{{ _id }}",
"platform":"mqtt"
Expand Down
59 changes: 49 additions & 10 deletions FiatChampAddon/FiatClient/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

builder.Configuration.AddEnvironmentVariables("FiatChamp_");

//todo: better button description
//todo: integrate reports and events
//todo: schedule turn charging off
//todo: better handling of auto refresh battery and location ...
Expand All @@ -36,6 +35,9 @@
.WriteTo.Console()
.CreateLogger();

Log.Information("Delay start for seconds: {0}", appConfig.StartDelaySeconds);
await Task.Delay(TimeSpan.FromSeconds(appConfig.StartDelaySeconds));

if (appConfig.Brand is FcaBrand.Ram or FcaBrand.Dodge)
{
Log.Warning("{0} support is experimental.", appConfig.Brand);
Expand All @@ -47,7 +49,7 @@ await app.RunAsync(async (CoconaAppContext ctx) =>
Log.Debug("{0}", appConfig.Dump());

IFiatClient fiatClient =
appConfig.UseFakeApi ? new FiatClientFake() : new FiatClient(appConfig.FiatUser, appConfig.FiatPw, FcaBrand.Ram);
appConfig.UseFakeApi ? new FiatClientFake() : new FiatClient(appConfig.FiatUser, appConfig.FiatPw, appConfig.Brand);

var mqttClient = new SimpleMqttClient(appConfig.MqttServer,
appConfig.MqttPort,
Expand Down Expand Up @@ -134,9 +136,11 @@ await app.RunAsync(async (CoconaAppContext ctx) =>

compactDetails.TryGetValue(unitKey, out var tmpUnit);

if (tmpUnit == "km" && shouldConvertKmToMiles)
if (tmpUnit == "km")
{
if (int.TryParse(detail.Value, out var kmValue))
sensor.DeviceClass = "distance";

if (shouldConvertKmToMiles && int.TryParse(detail.Value, out var kmValue))
{
var miValue = Math.Round(kmValue * 0.62137, 2);
sensor.Value = miValue.ToString(CultureInfo.InvariantCulture);
Expand All @@ -147,7 +151,7 @@ await app.RunAsync(async (CoconaAppContext ctx) =>
switch (tmpUnit)
{
case "volts":
sensor.Icon = "mdi:lightning-bolt";
sensor.DeviceClass = "voltage";
sensor.Unit = "V";
break;
case null or "null":
Expand All @@ -160,23 +164,45 @@ await app.RunAsync(async (CoconaAppContext ctx) =>
}

return sensor;
}).ToList();

}).ToDictionary(k => k.Name, v => v);

if (sensors.TryGetValue("car_evInfo_battery_stateOfCharge", out var stateOfChargeSensor))
{
stateOfChargeSensor.DeviceClass = "battery";
stateOfChargeSensor.Unit = "%";
}

if (sensors.TryGetValue("car_evInfo_battery_timeToFullyChargeL2", out var timeToFullyChargeSensor))
{
timeToFullyChargeSensor.DeviceClass = "duration";
timeToFullyChargeSensor.Unit = "min";
}

Log.Debug("Announce sensors: {0}", sensors.Dump());
Log.Information("Pushing new sensors and values to Home Assistant");

await Parallel.ForEachAsync(sensors, async (sensor, token) =>
await Parallel.ForEachAsync(sensors.Values, async (sensor, token) =>
{
await sensor.Announce();
});

Log.Debug("Waiting for home assistant to process all sensors");
await Task.Delay(TimeSpan.FromSeconds(5), ctx.CancellationToken);

await Parallel.ForEachAsync(sensors, async (sensor, token) =>
await Parallel.ForEachAsync(sensors.Values, async (sensor, token) =>
{
await sensor.PublishState();
});

var lastUpdate = new HaSensor(mqttClient, "LAST_UPDATE", haDevice)
{
Value = DateTime.Now.ToString("O"),
DeviceClass = "timestamp"
};

await lastUpdate.Announce();
await lastUpdate.PublishState();

var haEntities = persistentHaEntities.GetOrAdd(vehicle.Vin, s =>
CreateInteractiveEntities(fiatClient, mqttClient, vehicle, haDevice));

Expand Down Expand Up @@ -247,6 +273,12 @@ IEnumerable<HaEntity> CreateInteractiveEntities(IFiatClient fiatClient, SimpleMq
forceLoopResetEvent.Set();
});

var batteryRefreshButton = new HaButton(mqttClient, "RefreshBatteryStatus", haDevice, async button =>
{
if (await TrySendCommand(fiatClient, FiatCommand.DEEPREFRESH, vehicle.Vin))
forceLoopResetEvent.Set();
});

var deepRefreshButton = new HaButton(mqttClient, "DeepRefresh", haDevice, async button =>
{
if (await TrySendCommand(fiatClient, FiatCommand.DEEPREFRESH, vehicle.Vin))
Expand Down Expand Up @@ -285,6 +317,13 @@ IEnumerable<HaEntity> CreateInteractiveEntities(IFiatClient fiatClient, SimpleMq

return new HaEntity[]
{
hvacSwitch, trunkSwitch, chargeNowButton, deepRefreshButton, locateLightsButton, updateLocationButton, lockSwitch
hvacSwitch,
trunkSwitch,
chargeNowButton,
deepRefreshButton,
locateLightsButton,
updateLocationButton,
lockSwitch,
batteryRefreshButton
};
}
2 changes: 1 addition & 1 deletion FiatChampAddon/FiatClient/SimpleMqttClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,6 @@ public async Task Sub(string topic, Func<string, Task> callback)

public async Task Pub(string topic, string payload)
{
await _mqttClient.EnqueueAsync(topic, payload);
await _mqttClient.EnqueueAsync(topic, payload, retain: true);
}
}
6 changes: 0 additions & 6 deletions FiatChampAddon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,6 @@ inside the mqtt integration (click on "devices"). after a successful run there s

if not then check the error logs output of the addon.

### Why is location not working.

it should work. have a look at the attributes. only the main status says "unknown". haven't figured out yet how to fix that.

<img src="https://user-images.githubusercontent.com/30373916/196045834-0d57657a-3ef0-4361-9340-7946778158e7.png" width="300px">

### What is DeepRefresh ? How to update my battery charging level % ?

DeepRefresh is the "fiat language" for battery status update. The car sents only relatively rarely battery charging level % updates. If thats too slow for you press the "DeepRefresh" button (or call it in an automation) and the car should immediately update and sent back its current battery charging level %.
Expand Down
4 changes: 3 additions & 1 deletion FiatChampAddon/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ url: "https://github.com/wubbl0rz/FiatChamp"
description: "Connect your FIAT (uconnect) car to Home Assistant. πŸš—"
services:
- "mqtt:want"
version: "2.0.16"
version: "3.0.0"
homeassistant_api: true
image: "ghcr.io/wubbl0rz/image-{arch}-fiat-champ"
slug: "fiat_champ"
Expand All @@ -22,6 +22,7 @@ options:
AutoRefreshLocation: false
EnableDangerousCommands: false
CarUnknownLocation: "away"
StartDelaySeconds: 1
schema:
FiatUser: str
FiatPw: password
Expand All @@ -38,6 +39,7 @@ schema:
OverrideMqttServer: str?
OverrideMqttPort: int?
CarUnknownLocation: str
StartDelaySeconds: int?
arch:
- amd64
- armv7
1 change: 1 addition & 0 deletions FiatChampAddon/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ test "$FiatChamp_MqttPort" = "null" && export FiatChamp_MqttPort=$(bashio::servi

export FiatChamp_CarUnknownLocation=$(bashio::config 'CarUnknownLocation')
export FiatChamp_Brand=$(bashio::config 'Brand')
export FiatChamp_StartDelaySeconds=$(bashio::config 'StartDelaySeconds')

export FiatChamp_SupervisorToken=$SUPERVISOR_TOKEN

Expand Down
3 changes: 3 additions & 0 deletions FiatChampAddon/translations/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ configuration:
CarUnknownLocation:
name: Car unknown location status
description: Car location sensor status if car is not in a known zone.
StartDelaySeconds:
name: Start delay in seconds
description: Delay start of addon. Useful if you want to wait for other addons to start first (like slow mqtt startup).
ConvertKmToMiles:
name: Force convert km to miles
description: Not needed if your Home Assistant config is set to imperial system. Force try to convert kilometer values to miles.
Expand Down
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# ![image](https://user-images.githubusercontent.com/30373916/190129327-ca33228f-9864-418a-a65c-8be4de9592bc.png) FiatChamp πŸš—

Connect your FIAT, Jeep, Ram, Dodge car or truck to Home Assistant. Needs a car with uconnect services enabled and valid account.
Connect your FIAT, Jeep, Ram, Dodge car or truck to Home Assistant. Needs a vehicle with enabled uconnect services and valid account.

- Fiat: Works βœ…
- Jeep: Works βœ… (https://github.com/wubbl0rz/FiatChamp/issues/11)
- Ram Truck: Experimental ⚠️ (https://github.com/wubbl0rz/FiatChamp/issues/13)
- Jeep: Works βœ…
- Ram Truck: Experimental ⚠️
- Dodge: Unknown β›”

I have created this addon for my own car (new Fiat Icon 500e) and its the only one i can test it with.
Expand All @@ -18,21 +18,31 @@ Example dashboard using sensors and entities provided by this addon:

- Official Home Assistant MQTT Addon (recommended) running or external mqtt broker. Broker must be connected to Home Assistant MQTT integration.

It looks like there are different uconnect services. Make sure your car works with the following uconnect sites. Older vehicles that only uses mopar.com do not seem to work.
Fiat: https://myuconnect.fiat.com/
Jeep: https://myuconnect.jeep.com
Ram: https://connect.ramtrucks.com/
Dodge: https://connect.dodge.com

![image](https://user-images.githubusercontent.com/30373916/196045271-44287d3f-93ba-49c0-a72f-0bc92042efbb.png)

## Features

- Imports values like battery level, tyre pressure, odometet etc. into Home Assistant.
- Multiple Brands: Fiat, Jeep, Ram, Dodge
- Supports multiple cars on the same account.
- Location tracking.
- Home Assistant zones (home, work etc.) support.
- Uses the same data source as the official app.
- Remote commands (open doors, switch air conditioner on, ...) are supported since version 2.0. Some commands may not work with all cars. Available commands are:
- "UpdateLocation" (updates gps location from the car)
- "DeepRefresh" (gets battery charge % level)
- "UpdateLocation" (updates gps location of the car)
- "RefreshBatteryStatus" (refresh battery level %)
- "DeepRefresh" (same as "RefreshBatteryStatus")
- "Blink" (blink lights)
- "ChargeNOW" (starts charging)
- "Trunk" (open/close trunk lock)
- "HVAC" (turn on the temperature preconditioning in the car. __the official app does not support turning preconditioning off πŸ˜… i found an hidden command for this but i don't know if it will work or have negative side effects. enable it by setting the "EnableDangerousCommands" option.__)
- "DoorLock" (open/close doors. __See: "EnableDangerousCommands" option.__)
- "HVAC" (turn on the temperature preconditioning in the car. __the official app does not support turning preconditioning off πŸ˜… See: "EnableDangerousCommands" option.__)
- Convert km to miles option.

## What doesn't work (yet)?
Expand Down Expand Up @@ -82,13 +92,15 @@ if not then check the error logs output of the addon.

### Why is location not working.

it should work. have a look at the attributes. only the main status says "unknown". haven't figured out yet how to fix that.
it should work. have a look at the attributes. main status depends on the zones you configured in home assistant.
when the car is within the radius of a predefined zone at will show the zone name as location. otherwise status "away" or a custom string.

<img src="https://user-images.githubusercontent.com/30373916/196045834-0d57657a-3ef0-4361-9340-7946778158e7.png" width="300px">

### What is DeepRefresh ? How to update my battery charging level % ?

DeepRefresh is the "fiat language" for battery status update. The car sents only relatively rarely battery charging level % updates. If thats too slow for you press the "DeepRefresh" button (or call it in an automation) and the car should immediately update and sent back its current battery charging level %.
DeepRefresh is the "fiat language" for battery status update. The car sents only relatively rarely battery charging level % updates.
If thats too slow for you press the "RefreshBatteryStatus" or "DeepRefresh" button (or call it in an automation) and the car should immediately update and sent back its current battery charging level %.

![image](https://user-images.githubusercontent.com/30373916/196050176-8e9405ee-0caf-4fcc-a22b-ee5acc74e86f.png)

0 comments on commit 80ceb4a

Please sign in to comment.