diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 9326b35..eab337c 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -4,8 +4,6 @@ on: push: branches: - release - tags: - - v[0-9]+\.[0-9]+\.[0-9]+ jobs: build: @@ -17,6 +15,8 @@ jobs: - arch: "amd64" rid: "linux-musl-x64" runs-on: ubuntu-latest + permissions: + packages: write steps: - name: checkout uses: actions/checkout@v3 @@ -26,7 +26,7 @@ jobs: dotnet-version: 7.0.x - name: build_release run: | - cd TestAddon/App + cd FiatChampAddon/FiatClient dotnet publish -r ${{ matrix.rid }} --configuration Release --self-contained -o build/ - name: docker_login uses: docker/login-action@v2.0.0 @@ -38,6 +38,5 @@ jobs: uses: home-assistant/builder@2022.09.0 with: args: | - --test --${{ matrix.arch }} \ - --target TestAddon + --target FiatChampAddon diff --git a/FiatChampAddon/CHANGELOG.md b/FiatChampAddon/CHANGELOG.md index b563517..e24c136 100644 --- a/FiatChampAddon/CHANGELOG.md +++ b/FiatChampAddon/CHANGELOG.md @@ -1,5 +1,16 @@ # CHANGELOG +## 2.0.13 - 2022-10-16 +- km to miles conversion +- distance sensors export unit of measurement to home assistant (km or mi) +- more fitting icons for some sensors +- obfuscate mail account in log +- fix debug logging + +## 2.0.7 - 2022-10-10 +- supports remote door locking and unlocking. dangerous command... disabled by default. +- more logging + ## 2.0.6 - 2022-10-06 - fixed high memory usage with prebuilt image. as a bonus installation an upgrades should be a lot faster now. diff --git a/FiatChampAddon/Dockerfile b/FiatChampAddon/Dockerfile index 542372e..ea8a58f 100644 --- a/FiatChampAddon/Dockerfile +++ b/FiatChampAddon/Dockerfile @@ -2,15 +2,13 @@ ARG BUILD_FROM FROM $BUILD_FROM -ARG BUILD_ARCH - RUN apk add icu-libs RUN apk add icu COPY run.sh /run.sh RUN chmod a+x /run.sh -COPY FiatClient/build/$BUILD_ARCH/ /build/. +COPY FiatClient/build/ /build/. LABEL org.opencontainers.image.source https://github.com/wubbl0rz/FiatChamp diff --git a/FiatChampAddon/FiatClient/AppConfig.cs b/FiatChampAddon/FiatClient/AppConfig.cs index ba9885b..f7ab25e 100644 --- a/FiatChampAddon/FiatClient/AppConfig.cs +++ b/FiatChampAddon/FiatClient/AppConfig.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Text; +using System.Text.RegularExpressions; using Newtonsoft.Json; namespace FiatChamp; @@ -17,12 +18,18 @@ public record AppConfig public bool AutoRefreshLocation { get; set; } = false; public bool AutoRefreshBattery { get; set; } = false; public bool EnableDangerousCommands { get; set; } = false; + public bool ConvertKmToMiles { get; set; } = false; public bool DevMode { get; set; } = false; + public bool UseFakeApi { get; set; } = false; + public bool Debug { get; set; } = false; - public override string ToString() + public string ToStringWithoutSecrets() { + var user = this.FiatUser[0..2] + new string('*', this.FiatUser[2..].Length); + var tmp = this with { + FiatUser = user, FiatPw = new string('*', this.FiatPw.Length), MqttPw = new string('*', this.MqttPw.Length), FiatPin = new string('*', this.FiatPin?.Length ?? 0) @@ -30,7 +37,7 @@ public override string ToString() return JsonConvert.SerializeObject(tmp, Formatting.Indented); } - + public bool IsPinSet() { return !string.IsNullOrWhiteSpace(this.FiatPin); diff --git a/FiatChampAddon/FiatClient/FiatChamp.csproj b/FiatChampAddon/FiatClient/FiatChamp.csproj index 2b2a789..38e303d 100644 --- a/FiatChampAddon/FiatClient/FiatChamp.csproj +++ b/FiatChampAddon/FiatClient/FiatChamp.csproj @@ -16,6 +16,8 @@ + + diff --git a/FiatChampAddon/FiatClient/FiatClient.cs b/FiatChampAddon/FiatClient/FiatClient.cs index 227d89e..e29c835 100644 --- a/FiatChampAddon/FiatClient/FiatClient.cs +++ b/FiatChampAddon/FiatClient/FiatClient.cs @@ -2,12 +2,286 @@ using Amazon; using Amazon.CognitoIdentity; using Amazon.Runtime; -using FiatChamp; using Flurl; using Flurl.Http; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Serilog; -public class FiatClient +namespace FiatChamp; + +public interface IFiatClient +{ + Task LoginAndKeepSessionAlive(); + Task SendCommand(string vin, string command, string pin, string action); + Task Fetch(); +} + +public class FiatClientFake : IFiatClient +{ + public Task LoginAndKeepSessionAlive() + { + return Task.CompletedTask; + } + + public Task SendCommand(string vin, string command, string pin, string action) + { + return Task.CompletedTask; + } + + public Task Fetch() + { + var vehicle = JsonConvert.DeserializeObject(""" + { + "RegStatus": "COMPLETED_STAGE_3", + "Color": "BLUE", + "Year": 2022, + "TsoBodyCode": "", + "NavEnabledHu": false, + "Language": "", + "CustomerRegStatus": "Y", + "Radio": "", + "ActivationSource": "DEALER", + "Nickname": "KEKW", + "Vin": "LDM1SN7DHD7DHSHJ6753D", + "Company": "FCA", + "Model": 332, + "ModelDescription": "Neuer 500 3+1", + "TcuType": 2, + "Make": "FIAT", + "BrandCode": "12", + "SoldRegion": "EMEA" + } + """); + + vehicle.Details = JObject.Parse(""" + { + "vehicleInfo": { + "totalRangeADA": null, + "odometer": { + "odometer": { + "value": "1234", + "unit": "km" + } + }, + "daysToService": "null", + "fuel": { + "fuelAmountLevel": null, + "isFuelLevelLow": false, + "distanceToEmpty": { + "value": "150", + "unit": "km" + }, + "fuelAmount": { + "value": "null", + "unit": "null" + } + }, + "oilLevel": { + "oilLevel": null + }, + "tyrePressure": [ + { + "warning": false, + "pressure": { + "value": "null", + "unit": "kPa" + }, + "type": "FL", + "status": "NORMAL" + }, + { + "warning": false, + "pressure": { + "value": "null", + "unit": "kPa" + }, + "type": "FR", + "status": "NORMAL" + }, + { + "warning": false, + "pressure": { + "value": "null", + "unit": "kPa" + }, + "type": "RL", + "status": "NORMAL" + }, + { + "warning": false, + "pressure": { + "value": "null", + "unit": "kPa" + }, + "type": "RR", + "status": "NORMAL" + } + ], + "batteryInfo": { + "batteryStatus": "0", + "batteryVoltage": { + "value": "14.55", + "unit": "volts" + } + }, + "tripsInfo": { + "trips": [ + { + "totalElectricDistance": { + "value": "null", + "unit": "km" + }, + "name": "TripA", + "totalDistance": { + "value": "1013", + "unit": "km" + }, + "energyUsed": { + "value": "null", + "unit": "kmpl" + }, + "averageEnergyUsed": { + "value": "null", + "unit": "kmpl" + }, + "totalHybridDistance": { + "value": "null", + "unit": "km" + } + }, + { + "totalElectricDistance": { + "value": "null", + "unit": "km" + }, + "name": "TripB", + "totalDistance": { + "value": "14", + "unit": "km" + }, + "energyUsed": { + "value": "null", + "unit": "kmpl" + }, + "averageEnergyUsed": { + "value": "null", + "unit": "kmpl" + }, + "totalHybridDistance": { + "value": "null", + "unit": "km" + } + } + ] + }, + "batPwrUsageDisp": null, + "distanceToService": { + "distanceToService": { + "value": "5127.0", + "unit": "km" + } + }, + "wheelCount": 4, + "hvacPwrUsageDisp": null, + "mtrPwrUsageDisp": null, + "tpmsvehicle": false, + "hVBatSOH": null, + "isTPMSVehicle": false, + "timestamp": 1665779022952 + }, + "evInfo": { + "chargeSchedules": [], + "battery": { + "stateOfCharge": 72, + "chargingLevel": "LEVEL_2", + "plugInStatus": true, + "timeToFullyChargeL2": 205, + "chargingStatus": "CHARGING", + "totalRange": 172, + "distanceToEmpty": { + "value": 172, + "unit": "km" + } + }, + "timestamp": 1665822611085, + "schedules": [ + { + "chargeToFull": false, + "scheduleType": "NONE", + "enableScheduleType": false, + "scheduledDays": { + "sunday": false, + "saturday": false, + "tuesday": false, + "wednesday": false, + "thursday": false, + "friday": false, + "monday": false + }, + "startTime": "00:00", + "endTime": "00:00", + "cabinPriority": false, + "repeatSchedule": true + }, + { + "chargeToFull": false, + "scheduleType": "NONE", + "enableScheduleType": false, + "scheduledDays": { + "sunday": false, + "saturday": false, + "tuesday": false, + "wednesday": false, + "thursday": false, + "friday": false, + "monday": false + }, + "startTime": "00:00", + "endTime": "00:00", + "cabinPriority": false, + "repeatSchedule": true + }, + { + "chargeToFull": false, + "scheduleType": "NONE", + "enableScheduleType": false, + "scheduledDays": { + "sunday": false, + "saturday": false, + "tuesday": false, + "wednesday": false, + "thursday": false, + "friday": false, + "monday": false + }, + "startTime": "00:00", + "endTime": "00:00", + "cabinPriority": false, + "repeatSchedule": true + } + ] + }, + "timestamp": 1665822611085 + } + """); + + vehicle.Location = JsonConvert.DeserializeObject(""" + { + "TimeStamp": 1665779022952, + "Longitude": 4.1234365, + "Latitude": 69.4765989, + "Altitude": 40.346462111, + "Bearing": 0, + "IsLocationApprox": true + } + """); + + return Task.FromResult(new[] { vehicle }); + } +} + +public class FiatClient : IFiatClient { private readonly string _loginApiKey = "3_mOx_J2dRgjXYCdyhchv3b5lhi54eBcdCTX4BI8MORqmZCoQWhA0mV2PTlptLGUQI"; private readonly string _apiKey = "2wGyL6PHec9o1UeLPYpoYa1SkEWqeBur9bLsi24i"; @@ -51,13 +325,14 @@ public async Task LoginAndKeepSessionAlive() { try { - Console.WriteLine("REFRESH SESSION"); + Log.Information("REFRESH SESSION"); await this.Login(); } catch (Exception e) { - Console.WriteLine("ERROR WHILE REFRESH SESSION"); - e.Message.Dump(); + + Log.Error("ERROR WHILE REFRESH SESSION"); + Log.Debug("{0}", e); } } }); @@ -72,7 +347,7 @@ private async Task Login() .WithCookies(_cookieJar) .GetJsonAsync(); - loginResponse.Dump(); + Log.Debug("{0}", loginResponse.Dump()); loginResponse.ThrowOnError("Login failed."); @@ -90,7 +365,7 @@ private async Task Login() })) .ReceiveJson(); - authResponse.Dump(); + Log.Debug("{0}", authResponse.Dump()); authResponse.ThrowOnError("Authentication failed."); @@ -106,7 +381,7 @@ private async Task Login() .WithCookies(_cookieJar) .GetJsonAsync(); - jwtResponse.Dump(); + Log.Debug("{0}", jwtResponse.Dump()); jwtResponse.ThrowOnError("Authentication failed."); @@ -120,10 +395,10 @@ private async Task Login() }) .ReceiveJson(); + Log.Debug("{0}", identityResponse.Dump()); + identityResponse.ThrowOnError("Identity failed."); - identityResponse.Dump(); - var client = new AmazonCognitoIdentityClient(new AnonymousAWSCredentials(), RegionEndpoint.EUWest1); var res = await client.GetCredentialsForIdentityAsync(identityResponse.IdentityId, @@ -172,9 +447,8 @@ private Dictionary WithFiatDefaultParameter(Dictionary(); - pinAuthResponse.Dump(); + Log.Debug("{0}", pinAuthResponse.Dump()); var json = new { @@ -207,7 +481,7 @@ public async Task SendCommand(string vin, string command, string pin, string act .PostJsonAsync(json) .ReceiveJson(); - commandResponse.Dump(); + Log.Debug("{0}", commandResponse.Dump()); } public async Task Fetch() @@ -223,8 +497,8 @@ public async Task Fetch() .WithHeaders(WithAwsDefaultParameter(_apiKey)) .AwsSign(awsCredentials) .GetJsonAsync(); - - vehicleResponse.Dump(); + + Log.Debug("{0}", vehicleResponse.Dump()); foreach (var vehicle in vehicleResponse.Vehicles) { @@ -234,8 +508,8 @@ public async Task Fetch() .WithHeaders(WithAwsDefaultParameter(_apiKey)) .AwsSign(awsCredentials) .GetJsonAsync(); - - vehicleDetails.Dump(); + + Log.Debug("{0}", vehicleDetails.Dump()); vehicle.Details = vehicleDetails; @@ -248,9 +522,10 @@ public async Task Fetch() vehicle.Location = vehicleLocation; - vehicleLocation.Dump(); + Log.Debug("{0}", vehicleLocation.Dump()); } return vehicleResponse.Vehicles; } -} \ No newline at end of file +} + diff --git a/FiatChampAddon/FiatClient/FiatCommand.cs b/FiatChampAddon/FiatClient/FiatCommand.cs index 2f98cdb..a8a5cbe 100644 --- a/FiatChampAddon/FiatClient/FiatCommand.cs +++ b/FiatChampAddon/FiatClient/FiatCommand.cs @@ -8,6 +8,8 @@ public class FiatCommand public static readonly FiatCommand ROPRECOND_OFF = new() { Message = "ROPRECOND", IsDangerous = true }; public static readonly FiatCommand ROTRUNKUNLOCK = new() { Message = "ROTRUNKUNLOCK" }; public static readonly FiatCommand ROTRUNKLOCK = new() { Message = "ROTRUNKLOCK" }; + public static readonly FiatCommand RDU = new() { Message = "RDU", IsDangerous = true }; + public static readonly FiatCommand RDL = new() { Message = "RDL", IsDangerous = true }; public bool IsDangerous { get; set; } public required string Message { get; init; } diff --git a/FiatChampAddon/FiatClient/Helper.cs b/FiatChampAddon/FiatClient/Helper.cs index f776f3e..5e48ff1 100644 --- a/FiatChampAddon/FiatClient/Helper.cs +++ b/FiatChampAddon/FiatClient/Helper.cs @@ -55,9 +55,8 @@ public static IFlurlRequest AwsSign(this IFlurlRequest request, ImmutableCredent return request; } - - [Conditional("DEBUG")] - public static void Dump(this object o) + + public static string Dump(this object o) { try { @@ -73,22 +72,20 @@ public static void Dump(this object o) try { var json = JObject.Parse(str); - Console.WriteLine(json); + return json.ToString(Formatting.Indented); } catch (Exception e) { - // ignored + return str; } - - return; } - Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented)); - + return JsonConvert.SerializeObject(result, Formatting.Indented); + } catch (Exception) { - // ignored + return o.GetType().ToString(); } } } \ No newline at end of file diff --git a/FiatChampAddon/FiatClient/HomeAssistant.cs b/FiatChampAddon/FiatClient/HomeAssistant.cs index 683b82d..4f57745 100644 --- a/FiatChampAddon/FiatClient/HomeAssistant.cs +++ b/FiatChampAddon/FiatClient/HomeAssistant.cs @@ -6,8 +6,10 @@ public abstract class HaEntity protected readonly string _name; protected readonly HaDevice _haDevice; protected readonly string _id; + + public string Name => _name; - public HaEntity(SimpleMqttClient mqttClient, string name, HaDevice haDevice) + protected HaEntity(SimpleMqttClient mqttClient, string name, HaDevice haDevice) { _mqttClient = mqttClient; _name = name; @@ -74,9 +76,12 @@ await _mqttClient.Pub(_configTopic, $$""" public class HaSensor : HaEntity { public string Value { get; set; } = ""; + public string Icon { get; set; } = "mdi:eye"; + public string Unit { get; set; } = ""; + private readonly string _stateTopic; private readonly string _configTopic; - + public HaSensor(SimpleMqttClient mqttClient, string name, HaDevice haDevice) : base(mqttClient, name, haDevice) { _stateTopic = $"homeassistant/sensor/{_id}/state"; @@ -99,6 +104,8 @@ await _mqttClient.Pub(_configTopic, $$""" "name":"{{ _haDevice.Name }}", "sw_version":"{{ _haDevice.Version }}"}, "name":"{{ _name }}", + "unit_of_measurement":"{{ this.Unit }}", + "icon":"{{ this.Icon }}", "state_topic":"{{ _stateTopic }}", "unique_id":"{{ _id }}", "platform":"mqtt" diff --git a/FiatChampAddon/FiatClient/PollyHttpClientFactory.cs b/FiatChampAddon/FiatClient/PollyHttpClientFactory.cs index bfff0ef..baab963 100644 --- a/FiatChampAddon/FiatClient/PollyHttpClientFactory.cs +++ b/FiatChampAddon/FiatClient/PollyHttpClientFactory.cs @@ -1,6 +1,7 @@ using AwsSignatureVersion4; using Flurl.Http.Configuration; using Polly; +using Serilog; namespace FiatChamp; @@ -19,8 +20,9 @@ protected override Task SendAsync(HttpRequestMessage reques { var ex = delegateResult.Exception as HttpRequestException; var result = delegateResult.Result?.StatusCode.ToString() ?? ex?.StatusCode.ToString() ?? ex?.Message; - - Console.WriteLine($"Error connecting to {request.RequestUri}. Result: {result}. Retrying in {time}"); + + Log.Warning("Error connecting to {0}. Result: {1}. Retrying in {2}", + request.RequestUri, result, time); }); return retryPolicy.ExecuteAsync(ct => { return base.SendAsync(request, ct); }, cancellationToken); diff --git a/FiatChampAddon/FiatClient/Program.cs b/FiatChampAddon/FiatClient/Program.cs index a7d2c7e..b2c645e 100644 --- a/FiatChampAddon/FiatClient/Program.cs +++ b/FiatChampAddon/FiatClient/Program.cs @@ -1,15 +1,21 @@ using System.Collections.Concurrent; +using System.Globalization; using Cocona; using FiatChamp; using FiatChamp.HA; using Flurl.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json.Linq; +using Serilog; +using Serilog.Events; var builder = CoconaApp.CreateBuilder(); builder.Configuration.AddEnvironmentVariables("FiatChamp_"); +//todo: integrate reports and events + builder.Services.AddOptions() .Bind(builder.Configuration) .ValidateDataAnnotations() @@ -21,11 +27,18 @@ var appConfig = builder.Configuration.Get(); var forceLoopResetEvent = new AutoResetEvent(false); +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Is(appConfig.Debug ? LogEventLevel.Debug : LogEventLevel.Information) + .WriteTo.Console() + .CreateLogger(); + await app.RunAsync(async (CoconaAppContext ctx) => { - Console.WriteLine("===== CONFIG ===== \n\n{0}\n", appConfig); - - var fiatClient = new FiatClient(appConfig.FiatUser, appConfig.FiatPw); + Log.Information("{0}", appConfig.ToStringWithoutSecrets()); + Log.Debug("{0}", appConfig.Dump()); + + IFiatClient fiatClient = + appConfig.UseFakeApi ? new FiatClientFake() : new FiatClient(appConfig.FiatUser, appConfig.FiatPw); var mqttClient = new SimpleMqttClient(appConfig.MqttServer, appConfig.MqttPort, @@ -37,10 +50,10 @@ await app.RunAsync(async (CoconaAppContext ctx) => while (!ctx.CancellationToken.IsCancellationRequested) { - Console.WriteLine($"FETCH DATA... {DateTime.Now}"); - + Log.Information("Now fetching new data..."); + GC.Collect(); - + try { await fiatClient.LoginAndKeepSessionAlive(); @@ -51,14 +64,14 @@ await app.RunAsync(async (CoconaAppContext ctx) => { await TrySendCommand(fiatClient, FiatCommand.DEEPREFRESH, vehicle.Vin); } - + if (appConfig.AutoRefreshLocation) { await TrySendCommand(fiatClient, FiatCommand.VF, vehicle.Vin); } - + await Task.Delay(TimeSpan.FromSeconds(10), ctx.CancellationToken); - + var vehicleName = string.IsNullOrEmpty(vehicle.Nickname) ? "Car" : vehicle.Nickname; var suffix = appConfig.DevMode ? "DEV" : ""; @@ -77,43 +90,84 @@ await app.RunAsync(async (CoconaAppContext ctx) => Lon = vehicle.Location.Longitude }; + Log.Debug("Announce sensor: {0}", tracker.Dump()); await tracker.Announce(); await tracker.PublishState(); var compactDetails = vehicle.Details.Compact("car"); - await Parallel.ForEachAsync(compactDetails, async (sensorData, token) => + var sensors = compactDetails.Select(detail => { - var sensor = new HaSensor(mqttClient, sensorData.Key, haDevice) + var sensor = new HaSensor(mqttClient, detail.Key, haDevice) { - Value = sensorData.Value + Value = detail.Value }; - + + if (detail.Key.EndsWith("_value")) + { + var unitKey = detail.Key.Replace("_value", "_unit"); + + compactDetails.TryGetValue(unitKey, out var tmpUnit); + + if (appConfig.ConvertKmToMiles && tmpUnit == "km") + { + if (int.TryParse(detail.Value, out var kmValue)) + { + var miValue = kmValue * 0.62137; + sensor.Value = miValue.ToString(CultureInfo.InvariantCulture); + tmpUnit = "mi"; + } + } + + switch (tmpUnit) + { + case "volts": + sensor.Icon = "mdi:lightning-bolt"; + sensor.Unit = "V"; + break; + case null or "null": + sensor.Unit = ""; + break; + default: + sensor.Unit = tmpUnit; + break; + } + } + + return sensor; + }); + + await Parallel.ForEachAsync(sensors.ToList(), async (sensor, token) => + { + Log.Debug("Announce sensor: {0}", sensor.Dump()); await sensor.Announce(); await sensor.PublishState(); }); - var haEntities = persistentHaEntities.GetOrAdd(vehicle.Vin, s => - CreateEntities(fiatClient, mqttClient, vehicle, haDevice)); + var haEntities = persistentHaEntities.GetOrAdd(vehicle.Vin, s => + CreateInteractiveEntities(fiatClient, mqttClient, vehicle, haDevice)); foreach (var haEntity in haEntities) { + Log.Debug("Announce sensor: {0}", haEntity.Dump()); await haEntity.Announce(); } } } catch (FlurlHttpException httpException) { - Console.WriteLine($"Error connecting to the FIAT API. \n" + - $"This can happen from time to time. Retrying in {appConfig.RefreshInterval} minutes."); + Log.Warning($"Error connecting to the FIAT API. \n" + + $"This can happen from time to time. Retrying in {appConfig.RefreshInterval} minutes."); - httpException.Message.Dump(); + Log.Debug("{0}", httpException.Message); } catch (Exception e) { - Console.WriteLine(e.Message); + Log.Error("{0}", e); } + Log.Information("Fetching COMPLETED. Next update in {0} minutes.", appConfig.RefreshInterval); + WaitHandle.WaitAny(new[] { ctx.CancellationToken.WaitHandle, @@ -122,16 +176,16 @@ await Parallel.ForEachAsync(compactDetails, async (sensorData, token) => } }); -async Task TrySendCommand(FiatClient fiatClient, FiatCommand command, string vin) +async Task TrySendCommand(IFiatClient fiatClient, FiatCommand command, string vin) { - Console.WriteLine($"SEND COMMAND: {command.Message}"); + Log.Information("SEND COMMAND {0}: ", command.Message); var pin = appConfig.FiatPin ?? throw new Exception("PIN NOT SET"); if (command.IsDangerous && !appConfig.EnableDangerousCommands) { - Console.WriteLine($"{command.Message} not sent. " + - "Set \"EnableDangerousCommands\" option if you want to use it."); + Log.Warning("{0} not sent. " + + "Set \"EnableDangerousCommands\" option if you want to use it. ", command.Message); return false; } @@ -139,19 +193,20 @@ async Task TrySendCommand(FiatClient fiatClient, FiatCommand command, stri { await fiatClient.SendCommand(vin, command.Message, pin, command.Action); await Task.Delay(TimeSpan.FromSeconds(5)); - Console.WriteLine($"COMMAND: {command.Message} SUCCESSFUL"); + Log.Information("Command: {0} SUCCESSFUL", command.Message); } catch (Exception e) { - Console.WriteLine($"Error sending command: {command.Message}. Maybe wrong pin?"); - e.Message.Dump(); + Log.Error("Command: {0} ERROR. Maybe wrong pin?", command.Message); + Log.Debug("{0}", e); return false; } return true; } -IEnumerable CreateEntities(FiatClient fiatClient, SimpleMqttClient mqttClient, Vehicle vehicle, HaDevice haDevice) +IEnumerable CreateInteractiveEntities(IFiatClient fiatClient, SimpleMqttClient mqttClient, Vehicle vehicle, + HaDevice haDevice) { var updateLocationButton = new HaButton(mqttClient, "UpdateLocation", haDevice, async button => { @@ -189,6 +244,14 @@ IEnumerable CreateEntities(FiatClient fiatClient, SimpleMqttClient mqt forceLoopResetEvent.Set(); }); + var lockSwitch = new HaSwitch(mqttClient, "DoorLock", haDevice, async sw => + { + if (await TrySendCommand(fiatClient, sw.IsOn ? FiatCommand.RDL : FiatCommand.RDU, vehicle.Vin)) + forceLoopResetEvent.Set(); + }); + return new HaEntity[] - { hvacSwitch, trunkSwitch, chargeNowButton, deepRefreshButton, locateLightsButton, updateLocationButton }; + { + hvacSwitch, trunkSwitch, chargeNowButton, deepRefreshButton, locateLightsButton, updateLocationButton, lockSwitch + }; } \ No newline at end of file diff --git a/FiatChampAddon/FiatClient/SimpleMqttClient.cs b/FiatChampAddon/FiatClient/SimpleMqttClient.cs index b15e171..ad10b4b 100644 --- a/FiatChampAddon/FiatClient/SimpleMqttClient.cs +++ b/FiatChampAddon/FiatClient/SimpleMqttClient.cs @@ -2,6 +2,7 @@ using MQTTnet.Client; using MQTTnet.Extensions.ManagedClient; using MQTTnet.Protocol; +using Serilog; namespace FiatChamp; @@ -61,7 +62,7 @@ public async Task Sub(string topic, Func callback) } catch (Exception e) { - Console.WriteLine(e); + Log.Error("{0}", e); } } }; diff --git a/FiatChampAddon/config.yaml b/FiatChampAddon/config.yaml index 469b41d..5bec142 100644 --- a/FiatChampAddon/config.yaml +++ b/FiatChampAddon/config.yaml @@ -3,7 +3,7 @@ url: "https://github.com/wubbl0rz/FiatChamp" description: "Connect your FIAT (uconnect) car to Home Assistant. 🚗" services: - "mqtt:need" -version: "2.0.6" +version: "2.0.13" image: "ghcr.io/wubbl0rz/image-{arch}-fiat-champ" slug: "fiat_champ" init: false @@ -14,6 +14,7 @@ options: FiatPw: "" FiatPin: "" RefreshInterval: 15 + ConvertKmToMiles: false Debug: false AutoRefreshBattery: false AutoRefreshLocation: false @@ -22,6 +23,7 @@ schema: FiatUser: str FiatPw: password FiatPin: password? + ConvertKmToMiles: bool RefreshInterval: int Debug: bool AutoRefreshBattery: bool diff --git a/FiatChampAddon/run.sh b/FiatChampAddon/run.sh index fd3b8c8..ff98fde 100644 --- a/FiatChampAddon/run.sh +++ b/FiatChampAddon/run.sh @@ -9,11 +9,13 @@ export FiatChamp_FiatUser=$(bashio::config 'FiatUser') export FiatChamp_FiatPw=$(bashio::config 'FiatPw') export FiatChamp_FiatPin=$(bashio::config 'FiatPin') +export FiatChamp_ConvertKmToMiles=$(bashio::config 'ConvertKmToMiles') + export FiatChamp_AutoRefreshLocation=$(bashio::config 'AutoRefreshLocation') export FiatChamp_AutoRefreshBattery=$(bashio::config 'AutoRefreshBattery') export FiatChamp_EnableDangerousCommands=$(bashio::config 'EnableDangerousCommands') -DEBUG=$(bashio::config 'Debug') +export FiatChamp_Debug=$(bashio::config 'Debug') cd /build/ -./FiatChamp \ No newline at end of file +./FiatChamp diff --git a/FiatChampAddon/translations/en.yaml b/FiatChampAddon/translations/en.yaml index 3508fcd..8dd0b53 100644 --- a/FiatChampAddon/translations/en.yaml +++ b/FiatChampAddon/translations/en.yaml @@ -9,9 +9,15 @@ configuration: FiatPin: name: Pin description: Your PIN. Used for sending commands to the car. + ConvertKmToMiles: + name: Convert km to miles (EXPERIMENTAL) + description: Try to convert kilometer values to miles RefreshInterval: name: Refresh interval description: Fetch new data from api every N minutes. + Debug: + name: Debug + description: Enable debug logging. It will dump many informations to the log including session tokens and sensitive informations. EnableDangerousCommands: name: Dangerous commands. description: Enable commands that are potentially dangerous and/or experimental. Like unlocking doors or deactivate air conditioning. diff --git a/build.sh b/build.sh index 0b5d700..69283da 100755 --- a/build.sh +++ b/build.sh @@ -1,11 +1,8 @@ #!/usr/bin/env bash -cd FiatChampAddon/FiatClient/ +cp README.md FiatChampAddon/DOCS.md +cp README.md FiatChampAddon/. -~/.dotnet/dotnet publish --configuration Release -r linux-musl-x64 --self-contained=true -o build/amd64 -~/.dotnet/dotnet publish --configuration Release -r linux-musl-arm --self-contained=true -o build/armv7 +VERSION=$(cat FiatChampAddon/config.yaml| grep version | grep -P -o "[\d\.]*") -cd ../../ - -docker run --rm --privileged multiarch/qemu-user-static --reset -p yes -docker run --rm --privileged -v ~/.docker:/root/.docker -v $(pwd)/FiatChampAddon:/data homeassistant/amd64-builder --all -t /data +git tag -a $VERSION -m $VERSION