Skip to content

Commit

Permalink
Merge pull request #80 from pkuehnel/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
pkuehnel authored Apr 22, 2022
2 parents 9f856bd + f1d2e71 commit 836aa53
Show file tree
Hide file tree
Showing 21 changed files with 389 additions and 118 deletions.
12 changes: 12 additions & 0 deletions Plugins.SmaEnergymeter/Controllers/HelloController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Mvc;

namespace Plugins.SmaEnergymeter.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HelloController : ControllerBase
{
[HttpGet]
public Task<bool> IsAlive() => Task.FromResult(true);
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ If your SmartMeter does not have a REST Endpoint as needed you can use plugins:
[![Docker version](https://img.shields.io/docker/v/pkuehnel/smartteslaampsettersmaplugin/latest)](https://hub.docker.com/r/pkuehnel/smartteslaampsettersmaplugin)
[![Docker size](https://img.shields.io/docker/image-size/pkuehnel/smartteslaampsettersmaplugin/latest)](https://hub.docker.com/r/pkuehnel/smartteslaampsettersmaplugin)
[![Docker pulls](https://img.shields.io/docker/pulls/pkuehnel/smartteslaampsettersmaplugin)](https://hub.docker.com/r/pkuehnel/smartteslaampsettersmaplugin)

With the SMA Energymeter Plugin (note: Every SMA Home Manager 2.0 has an integrated EnergyMeter Interface, so this plugin is working with SMA Home Manager 2.0 as well) a new service is created, which receives the EnergyMeter values and averages them for the last x seconds. The URL of the endpoint is: http://ip-of-your-host:8453/api/CurrentPower?lastXSeconds=30
To use the plugin add the following to your `docker-compose.yml`:
```yaml
Expand Down
44 changes: 44 additions & 0 deletions SmartTeslaAmpSetter.Tests/Services/ChargingService.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Autofac;
using SmartTeslaAmpSetter.Shared.Dtos.Settings;
using SmartTeslaAmpSetter.Shared.Enums;
using SmartTeslaAmpSetter.Shared.TimeProviding;
using Xunit;
using Xunit.Abstractions;
using CarState = SmartTeslaAmpSetter.Shared.Dtos.Settings.CarState;

namespace SmartTeslaAmpSetter.Tests.Services;

Expand Down Expand Up @@ -162,6 +164,48 @@ public void Gets_relevant_car_IDs()
Assert.Single(relevantIds);
}

[Fact]
public void Gets_irrelevant_cars()
{
var geofence = "Home";
var cars = new List<Car>()
{
new Car()
{
Id = 1,
CarState = new CarState()
{
Geofence = geofence,
PluggedIn = true,
ClimateOn = false,
ChargerActualCurrent = 3,
SoC = 30,
SocLimit = 60,
},
},
new Car()
{
Id = 2,
CarState = new CarState()
{
Geofence = null,
PluggedIn = true,
ClimateOn = false,
ChargerActualCurrent = 3,
SoC = 30,
SocLimit = 60,
},
},
};
Mock.Mock<ISettings>().Setup(s => s.Cars).Returns(cars);
var chargingService = Mock.Create<Server.Services.ChargingService>();

var irrelevantCars = chargingService.GetIrrelevantCars(new List<int>(){1});

Assert.Single(irrelevantCars);
Assert.Contains(2, irrelevantCars.Select(c => c.Id));
}

private Car CreateDemoCar(ChargeMode chargeMode, DateTime latestTimeToReachSoC, int soC, int minimumSoC, bool autoFullSpeedCharge)
{
var car = new Car()
Expand Down
16 changes: 16 additions & 0 deletions SmartTeslaAmpSetter/Client/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,22 @@ else
@car.CarState.Name
</td>
</tr>
<tr>
<td>
StateString
</td>
<td>
@car.CarState.StateString
</td>
</tr>
<tr>
<td>
AutoFullspeedCharging
</td>
<td>
@car.CarState.AutoFullSpeedCharge
</td>
</tr>
<tr>
<td>
Last Set Amp
Expand Down
5 changes: 5 additions & 0 deletions SmartTeslaAmpSetter/Client/SmartTeslaAmpSetter.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
</PropertyGroup>

<ItemGroup>
Expand All @@ -16,4 +17,8 @@
<ProjectReference Include="..\Shared\SmartTeslaAmpSetter.Shared.csproj" />
</ItemGroup>

<ItemGroup>
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
</ItemGroup>

</Project>
Binary file modified SmartTeslaAmpSetter/Client/wwwroot/favicon.ico
Binary file not shown.
Binary file modified SmartTeslaAmpSetter/Client/wwwroot/icon-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added SmartTeslaAmpSetter/Client/wwwroot/icon-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions SmartTeslaAmpSetter/Client/wwwroot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="SmartTeslaAmpSetter.Client.styles.css" rel="stylesheet" />
<link href="manifest.json" rel="manifest" />
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
<script src="https://kit.fontawesome.com/f3249a5578.js" crossorigin="anonymous"></script>
</head>

Expand All @@ -21,6 +24,7 @@
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script>navigator.serviceWorker.register('service-worker.js');</script>
<link href="_content/Blazored.Toast/blazored-toast.min.css" rel="stylesheet"/>
</body>

Expand Down
20 changes: 20 additions & 0 deletions SmartTeslaAmpSetter/Client/wwwroot/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "Smart Tesla Amp Setter",
"short_name": "SmartTelsaAmpSetter",
"start_url": "./",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#03173d",
"icons": [
{
"src": "icon-512.png",
"type": "image/png",
"sizes": "512x512"
},
{
"src": "icon-192.png",
"type": "image/png",
"sizes": "192x192"
}
]
}
5 changes: 5 additions & 0 deletions SmartTeslaAmpSetter/Client/wwwroot/service-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// In development, always fetch from the network and do not enable offline support.
// This is because caching would make development more difficult (changes would not
// be reflected on the first load after each change).
// ReSharper disable once Html.EventNotResolved
self.addEventListener('fetch', () => { });
48 changes: 48 additions & 0 deletions SmartTeslaAmpSetter/Client/wwwroot/service-worker.published.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Caution! Be sure you understand the caveats before publishing an application with
// offline support. See https://aka.ms/blazor-offline-considerations

self.importScripts('./service-worker-assets.js');
self.addEventListener('install', event => event.waitUntil(onInstall(event)));
self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
self.addEventListener('fetch', event => event.respondWith(onFetch(event)));

const cacheNamePrefix = 'offline-cache-';
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
const offlineAssetsInclude = [/\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/];
const offlineAssetsExclude = [/^service-worker\.js$/];

async function onInstall(event) {
console.info('Service worker: Install');

// Fetch and cache all matching items from the assets manifest
const assetsRequests = self.assetsManifest.assets
.filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
.filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
.map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' }));
await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
}

async function onActivate(event) {
console.info('Service worker: Activate');

// Delete unused caches
const cacheKeys = await caches.keys();
await Promise.all(cacheKeys
.filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName)
.map(key => caches.delete(key)));
}

async function onFetch(event) {
let cachedResponse = null;
if (event.request.method === 'GET') {
// For all navigation requests, try to serve index.html from cache
// If you need some URLs to be server-rendered, edit the following check to exclude those URLs
const shouldServeIndexHtml = event.request.mode === 'navigate';

const request = shouldServeIndexHtml ? 'index.html' : event.request;
const cache = await caches.open(cacheName);
cachedResponse = await cache.match(request);
}

return cachedResponse || fetch(event.request);
}
11 changes: 11 additions & 0 deletions SmartTeslaAmpSetter/Server/Contracts/ITeslaService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using SmartTeslaAmpSetter.Shared.Enums;

namespace SmartTeslaAmpSetter.Server.Contracts;

public interface ITeslaService
{
Task StartCharging(int carId, int startAmp, CarState? carState);
Task WakeUpCar(int carId);
Task StopCharging(int carId);
Task SetAmp(int carId, int amps);
}
12 changes: 12 additions & 0 deletions SmartTeslaAmpSetter/Server/Controllers/HelloController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Mvc;

namespace SmartTeslaAmpSetter.Server.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HelloController : ControllerBase
{
[HttpGet]
public Task<bool> IsAlive() => Task.FromResult(true);
}
}
42 changes: 40 additions & 2 deletions SmartTeslaAmpSetter/Server/MqttHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using MQTTnet;
using MQTTnet.Client;
using SmartTeslaAmpSetter.Shared.Dtos.Settings;
using CarState = SmartTeslaAmpSetter.Shared.Enums.CarState;

namespace SmartTeslaAmpSetter.Server;

Expand All @@ -12,17 +13,29 @@ public class MqttHelper
private readonly MqttFactory _mqttFactory;
private readonly ISettings _settings;

// ReSharper disable once InconsistentNaming
private const string TopicDisplayName = "display_name";
// ReSharper disable once InconsistentNaming
private const string TopicSoc = "battery_level";
// ReSharper disable once InconsistentNaming
private const string TopicChargeLimit = "charge_limit_soc";
// ReSharper disable once InconsistentNaming
private const string TopicGeofence = "geofence";
// ReSharper disable once InconsistentNaming
private const string TopicChargerPhases = "charger_phases";
// ReSharper disable once InconsistentNaming
private const string TopicChargerVoltage = "charger_voltage";
// ReSharper disable once InconsistentNaming
private const string TopicChargerActualCurrent = "charger_actual_current";
// ReSharper disable once InconsistentNaming
private const string TopicPluggedIn = "plugged_in";
// ReSharper disable once InconsistentNaming
private const string TopicIsClimateOn = "is_climate_on";
// ReSharper disable once InconsistentNaming
private const string TopicTimeToFullCharge = "time_to_full_charge";
// ReSharper disable once InconsistentNaming
private const string TopicState = "state";
// ReSharper disable once InconsistentNaming
private const string TopicHealthy = "healthy";
//ToDo: Add after next TeslaMateRelease
//private const string TopicChargeCurrentRequest = "charge_current_request";
Expand Down Expand Up @@ -202,8 +215,33 @@ private void UpdateCar(TeslaMateValue value)
}
break;
case TopicState:
car.CarState.State = value.Value;
_logger.LogDebug("New car state detected {car state}", car.CarState.State);
car.CarState.StateString = value.Value;
switch (value.Value)
{
case "asleep":
car.CarState.State = CarState.Asleep;
break;
case "offline":
car.CarState.State = CarState.Offline;
break;
case "online":
car.CarState.State = CarState.Online;
break;
case "charging":
car.CarState.State = CarState.Charging;
break;
case "suspended":
car.CarState.State = CarState.Suspended;
break;
case "driving":
car.CarState.State = CarState.Driving;
break;
default:
_logger.LogWarning("Unknown car state deteckted: {carState}", value.Value);
car.CarState.State = CarState.Unknown;
break;
}
_logger.LogDebug("New car state detected {car state}", car.CarState.StateString);
break;
case TopicHealthy:
car.CarState.Healthy = Convert.ToBoolean(value.Value);
Expand Down
1 change: 1 addition & 0 deletions SmartTeslaAmpSetter/Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
.AddTransient<IDateTimeProvider, DateTimeProvider>()
.AddTransient<IChargeTimeUpdateService, ChargeTimeUpdateService>()
.AddTransient<ITelegramService, TelegramService>()
.AddTransient<ITeslaService, TeslamateApiService>()
.AddSingleton<ISettings, Settings>()
.AddSingleton(mqttClient)
.AddTransient<MqttFactory>()
Expand Down
Loading

0 comments on commit 836aa53

Please sign in to comment.