diff --git a/Plugins.Modbus/Plugins.Modbus.csproj b/Plugins.Modbus/Plugins.Modbus.csproj index 77ca746aa..beb2c49a5 100644 --- a/Plugins.Modbus/Plugins.Modbus.csproj +++ b/Plugins.Modbus/Plugins.Modbus.csproj @@ -9,13 +9,15 @@ - - - - + + + + + - - + + + diff --git a/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj b/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj index 11c8d1b6b..22aa752a3 100644 --- a/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj +++ b/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj @@ -9,11 +9,12 @@ - - - - - + + + + + + diff --git a/Plugins.SolarEdge/Plugins.SolarEdge.csproj b/Plugins.SolarEdge/Plugins.SolarEdge.csproj index f22cae188..4038113a4 100644 --- a/Plugins.SolarEdge/Plugins.SolarEdge.csproj +++ b/Plugins.SolarEdge/Plugins.SolarEdge.csproj @@ -9,12 +9,13 @@ - + - - + + - + + diff --git a/Plugins.Solax/Plugins.Solax.csproj b/Plugins.Solax/Plugins.Solax.csproj index 87f606ff9..c7200d080 100644 --- a/Plugins.Solax/Plugins.Solax.csproj +++ b/Plugins.Solax/Plugins.Solax.csproj @@ -8,12 +8,13 @@ - - - - + + + + - + + diff --git a/README.md b/README.md index b98cde8d8..69192cb45 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,13 @@ [![edgeRelease](https://github.com/pkuehnel/TeslaSolarCharger/actions/workflows/edgeRelease.yml/badge.svg)](https://github.com/pkuehnel/TeslaSolarCharger/actions/workflows/edgeRelease.yml) -TeslaSolarCharger is a service to set one or multiple Teslas' charging current using the datalogger **[TeslaMate](https://github.com/adriankumpf/teslamate)**. +TeslaSolarCharger is a service to set one or multiple Teslas' charging current. ## Table of Contents - [How to install](#how-to-install) - [Docker compose](#docker-compose) - - [Setting up TeslaMate including TeslaSolarCharger](#Setting-up-TeslaMate-including-TeslaSolarCharger) + - [Setting up TeslaSolarCharger](#Setting-up-TeslaSolarCharger) - [docker-compose.yml content](#docker-composeyml-content) - [First startup of the application](#first-startup-of-the-application) - [Install and setup BLE API](#install-and-setup-ble-api) @@ -34,7 +34,7 @@ You can either install the software in a Docker container or download the binari The easiest way to use TeslaSolarCharger is with Docker. -Depending on your system, you have to install Docker first. To do this on a RaspberryPi (should be the same on standard Linux systems), you need to execute the following commands in your Terminal window: +Depending on your system, you have to install Docker first. To do this on a Raspberry Pi (should be the same on standard Linux systems), you need to execute the following commands in your Terminal window: 1. Install Docker ``` curl -sSL https://get.docker.com | sh @@ -50,99 +50,24 @@ Depending on your system, you have to install Docker first. To do this on a Rasp ``` If any issues occur, try to identify them using [this more detailed instruction](https://www.simplilearn.com/tutorials/docker-tutorial/raspberry-pi-docker). -If you are using a Windows host, install the Software from [here](https://docs.docker.com/desktop/install/windows-install/). Windows 11 is highly recommended. Select Linux Containers in the installation process. +If you are using a Windows host, install the Software from [here](https://docs.docker.com/desktop/install/windows-install/). Windows 11 is highly recommended. Select Linux Containers in the installation process. Note: The SMA plugin is not supported on Docker on Windows. -### Setting up TeslaMate including TeslaSolarCharger +### Setting up TeslaSolarCharger -To set up TeslaSolarCharger, you must create a `docker-compose.yml` (name is important!) file in a new directory. Note: During the setup, some additional data folders to persist data will be created in that folder, so it is recommended to use a new directory for your `docker-compose.yml`. +To set up TeslaSolarCharger, you must create a `docker-compose.yml` (name is important!) file in a new directory. #### docker-compose.yml content -The needed content of your `docker-compose.yml` depends on your inverter. By default, TeslaSolarCharger can consume JSON/XML REST APIs. To get the software running on [SMA](https://www.sma.de/) or [SolarEdge](https://www.solaredge.com/), you can use specific plugins which create the needed JSON API. You can use the software with any ModbusTCP-capable inverter also. +The required content of your `docker-compose.yml` depends on your inverter. By default, TeslaSolarCharger can consume JSON/XML REST APIs, MQTT messages or Modbus TCP. To get the software running on [SMA](https://www.sma.de/), [SolarEdge](https://www.solaredge.com/) or Solax based inverters, you can use specific plugins which create the required JSON API. ##### Content without using a plugin -Below you can see the content for your `docker-compose.yml` if you are not using any plugin. Note: I recommend changing as few things as possible on this file as this will increase the effort to set everything up but feel free to change the database password, encryption key, and Timezone. Important: If you change the password or the encryption key, you need to use the same password and encryption key at all points in your `docker-compose.yml` +Below you can see the content for your `docker-compose.yml` if you are not using any plugin. Note: I recommend changing as few things as possible on this file as this will increase the effort to set everything up but feel free to change the Timezone. ```yaml version: '3.3' services: - teslamate: - image: teslamate/teslamate:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - - TZ=Europe/Berlin ##You can change your Timezone here - ports: - - 4000:4000 - volumes: - - ./import:/opt/app/import - cap_drop: - - all - - database: - image: postgres:15 - restart: always - environment: - - POSTGRES_USER=teslamate - - POSTGRES_PASSWORD=secret ##You can change your password here - - POSTGRES_DB=teslamate - volumes: - - teslamate-db:/var/lib/postgresql/data - - grafana: - image: teslamate/grafana:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - ports: - - 3100:3000 - volumes: - - teslamate-grafana-data:/var/lib/grafana - - mosquitto: - image: eclipse-mosquitto:2 - restart: always - command: mosquitto -c /mosquitto-no-auth.conf - #ports: - # - 1883:1883 - volumes: - - mosquitto-conf:/mosquitto/config - - mosquitto-data:/mosquitto/data - - teslamateapi: - image: tobiasehlert/teslamateapi:latest - logging: - driver: "json-file" - options: - max-file: "5" - max-size: "10m" - restart: always - depends_on: - - database - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - TZ=Europe/Berlin ##You can change your Timezone here - - ENABLE_COMMANDS=true - - COMMANDS_ALL=true - - API_TOKEN_DISABLE=true - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - #ports: - # - 8080:8080 - teslasolarcharger: image: pkuehnel/teslasolarcharger:latest container_name: teslasolarcharger @@ -152,8 +77,6 @@ services: max-file: "10" max-size: "100m" restart: always - depends_on: - - teslamateapi environment: # - Serilog__MinimumLevel__Default=Verbose #uncomment this line and recreate container with docker compose up -d for more detailed logs - TZ=Europe/Berlin ##You can change your Timezone here @@ -163,10 +86,6 @@ services: - teslasolarcharger-configs:/app/configs volumes: - teslamate-db: - teslamate-grafana-data: - mosquitto-conf: - mosquitto-data: teslasolarcharger-configs: ``` @@ -178,7 +97,7 @@ volumes: [![Docker pulls](https://img.shields.io/docker/pulls/pkuehnel/teslasolarchargersmaplugin)](https://hub.docker.com/r/pkuehnel/teslasolarchargersmaplugin) The SMA plugin is used to access your EnergyMeter (or Sunny Home Manager 2.0) values. -To use the plugin, add these lines to the bottom of your `docker-compose.yml`. +To use the plugin, add these lines before the volumes section of your `docker-compose.yml`. Note: The SMA plugin is not supported on Docker on Windows. ```yaml smaplugin: @@ -202,83 +121,7 @@ You can also copy the complete content from here: ```yaml version: '3.3' - -services: - teslamate: - image: teslamate/teslamate:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - - TZ=Europe/Berlin ##You can change your Timezone here - ports: - - 4000:4000 - volumes: - - ./import:/opt/app/import - cap_drop: - - all - - database: - image: postgres:15 - restart: always - environment: - - POSTGRES_USER=teslamate - - POSTGRES_PASSWORD=secret ##You can change your password here - - POSTGRES_DB=teslamate - volumes: - - teslamate-db:/var/lib/postgresql/data - - grafana: - image: teslamate/grafana:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - ports: - - 3100:3000 - volumes: - - teslamate-grafana-data:/var/lib/grafana - - mosquitto: - image: eclipse-mosquitto:2 - restart: always - command: mosquitto -c /mosquitto-no-auth.conf - #ports: - # - 1883:1883 - volumes: - - mosquitto-conf:/mosquitto/config - - mosquitto-data:/mosquitto/data - - teslamateapi: - image: tobiasehlert/teslamateapi:latest - logging: - driver: "json-file" - options: - max-file: "5" - max-size: "10m" - restart: always - depends_on: - - database - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - TZ=Europe/Berlin ##You can change your Timezone here - - ENABLE_COMMANDS=true - - COMMANDS_ALL=true - - API_TOKEN_DISABLE=true - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - #ports: - # - 8080:8080 - +services: teslasolarcharger: image: pkuehnel/teslasolarcharger:latest container_name: teslasolarcharger @@ -288,8 +131,6 @@ services: max-file: "10" max-size: "100m" restart: always - depends_on: - - teslamateapi environment: # - Serilog__MinimumLevel__Default=Verbose #uncomment this line and recreate container with docker compose up -d for more detailed logs - TZ=Europe/Berlin ##You can change your Timezone here @@ -312,10 +153,6 @@ services: - ASPNETCORE_URLS=http://+:7192 volumes: - teslamate-db: - teslamate-grafana-data: - mosquitto-conf: - mosquitto-data: teslasolarcharger-configs: ``` @@ -328,9 +165,9 @@ volumes: [![Docker size](https://img.shields.io/docker/image-size/pkuehnel/teslasolarchargersolaredgeplugin/latest)](https://hub.docker.com/r/pkuehnel/teslasolarchargersolaredgeplugin) [![Docker pulls](https://img.shields.io/docker/pulls/pkuehnel/teslasolarchargersolaredgeplugin)](https://hub.docker.com/r/pkuehnel/teslasolarchargersolaredgeplugin) -The SolarEdge Plugin uses the cloud API, which is limited to 300 which is reset after 15 minutes. When the limit is reached the solaredge API does not gather any new values. This results in TSC displaying 0 grid and home battery power until 15 minutes are over. +The SolarEdge Plugin uses the cloud API, which is limited to 300 which is reset after 15 minutes. When the limit is reached the SolarEdge API does not gather any new values. This results in TSC displaying 0 grid and home battery power until 15 minutes are over. -To use the plugin, just add these lines to the bottom of your `docker-compose.yml`. Note: You have to change your site ID and your API key in the `CloudUrl` environment variable +To use the plugin, just add these lines before the volumes section of your `docker-compose.yml`. Note: You have to change your site ID and your API key in the `CloudUrl` environment variable ```yaml solaredgeplugin: @@ -355,84 +192,7 @@ You can also copy the complete content from here: ```yaml version: '3.3' - -services: - teslamate: - image: teslamate/teslamate:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - - TZ=Europe/Berlin ##You can change your Timezone here - ports: - - 4000:4000 - volumes: - - ./import:/opt/app/import - cap_drop: - - all - - database: - image: postgres:15 - restart: always - environment: - - POSTGRES_USER=teslamate - - POSTGRES_PASSWORD=secret ##You can change your password here - - POSTGRES_DB=teslamate - volumes: - - teslamate-db:/var/lib/postgresql/data - - grafana: - image: teslamate/grafana:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - ports: - - 3100:3000 - volumes: - - teslamate-grafana-data:/var/lib/grafana - - mosquitto: - image: eclipse-mosquitto:2 - restart: always - command: mosquitto -c /mosquitto-no-auth.conf - #ports: - # - 1883:1883 - volumes: - - mosquitto-conf:/mosquitto/config - - mosquitto-data:/mosquitto/data - - - teslamateapi: - image: tobiasehlert/teslamateapi:latest - logging: - driver: "json-file" - options: - max-file: "5" - max-size: "10m" - restart: always - depends_on: - - database - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - TZ=Europe/Berlin ##You can change your Timezone here - - ENABLE_COMMANDS=true - - COMMANDS_ALL=true - - API_TOKEN_DISABLE=true - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - #ports: - # - 8080:8080 - +services: teslasolarcharger: image: pkuehnel/teslasolarcharger:latest container_name: teslasolarcharger @@ -442,8 +202,6 @@ services: max-file: "10" max-size: "100m" restart: always - depends_on: - - teslamateapi environment: # - Serilog__MinimumLevel__Default=Verbose #uncomment this line and recreate container with docker compose up -d for more detailed logs - TZ=Europe/Berlin ##You can change your Timezone here @@ -467,177 +225,19 @@ services: - 7193:80 volumes: - teslamate-db: - teslamate-grafana-data: - mosquitto-conf: - mosquitto-data: teslasolarcharger-configs: ``` -##### Content using Modbus plugin - -[![Docker version](https://img.shields.io/docker/v/pkuehnel/teslasolarchargermodbusplugin/latest)](https://hub.docker.com/r/pkuehnel/teslasolarchargermodbusplugin) -[![Docker size](https://img.shields.io/docker/image-size/pkuehnel/teslasolarchargermodbusplugin/latest)](https://hub.docker.com/r/pkuehnel/teslasolarchargermodbusplugin) -[![Docker pulls](https://img.shields.io/docker/pulls/pkuehnel/teslasolarchargermodbusplugin)](https://hub.docker.com/r/pkuehnel/teslasolarchargermodbusplugin) - -You can also use the Modbus plugin. This is a general plugin, so don't be surprised if it does not work as expected right after starting up. Feel free to share your configurations [here](https://github.com/pkuehnel/TeslaSolarCharger/discussions/174) so I can add templates for future users. - -To use the plugin, just add these lines to the bottom of your `docker-compose.yml`. Note: As some inverters struggle with too many requests within a specific time, you can change the `RequestBlockMilliseconds` environment variable. - -```yaml - modbusplugin: - image: pkuehnel/teslasolarchargermodbusplugin:latest - container_name: teslasolarcharger_modbusplugin - logging: - driver: "json-file" - options: - max-file: "5" - max-size: "10m" - restart: always - environment: - - RequestBlockMilliseconds=0 - ports: - - 7191:80 - -``` - -You can also copy the complete content from here: -
- Complete file using Modbus plugin - -```yaml -version: '3.3' - -services: - teslamate: - image: teslamate/teslamate:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - - TZ=Europe/Berlin ##You can change your Timezone here - ports: - - 4000:4000 - volumes: - - ./import:/opt/app/import - cap_drop: - - all - - database: - image: postgres:15 - restart: always - environment: - - POSTGRES_USER=teslamate - - POSTGRES_PASSWORD=secret ##You can change your password here - - POSTGRES_DB=teslamate - volumes: - - teslamate-db:/var/lib/postgresql/data - - grafana: - image: teslamate/grafana:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - ports: - - 3100:3000 - volumes: - - teslamate-grafana-data:/var/lib/grafana - - mosquitto: - image: eclipse-mosquitto:2 - restart: always - command: mosquitto -c /mosquitto-no-auth.conf - #ports: - # - 1883:1883 - volumes: - - mosquitto-conf:/mosquitto/config - - mosquitto-data:/mosquitto/data - - teslamateapi: - image: tobiasehlert/teslamateapi:latest - logging: - driver: "json-file" - options: - max-file: "5" - max-size: "10m" - restart: always - depends_on: - - database - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - TZ=Europe/Berlin ##You can change your Timezone here - - ENABLE_COMMANDS=true - - COMMANDS_ALL=true - - API_TOKEN_DISABLE=true - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - #ports: - # - 8080:8080 - - teslasolarcharger: - image: pkuehnel/teslasolarcharger:latest - container_name: teslasolarcharger - logging: - driver: "json-file" - options: - max-file: "10" - max-size: "100m" - restart: always - depends_on: - - teslamateapi - environment: -# - Serilog__MinimumLevel__Default=Verbose #uncomment this line and recreate container with docker compose up -d for more detailed logs - - TZ=Europe/Berlin ##You can change your Timezone here - ports: - - 7190:80 - volumes: - - teslasolarcharger-configs:/app/configs - - modbusplugin: - image: pkuehnel/teslasolarchargermodbusplugin:latest - container_name: teslasolarcharger_modbusplugin - logging: - driver: "json-file" - options: - max-file: "5" - max-size: "10m" - restart: always - environment: - - RequestBlockMilliseconds=0 - ports: - - 7191:80 - -volumes: - teslamate-db: - teslamate-grafana-data: - mosquitto-conf: - mosquitto-data: - teslasolarcharger-configs: -``` - -
- - ##### Content using Solax plugin [![Docker version](https://img.shields.io/docker/v/pkuehnel/teslasolarchargersolaxplugin/latest)](https://hub.docker.com/r/pkuehnel/teslasolarchargersolaxplugin) [![Docker size](https://img.shields.io/docker/image-size/pkuehnel/teslasolarchargersolaxplugin/latest)](https://hub.docker.com/r/pkuehnel/teslasolarchargersolaxplugin) [![Docker pulls](https://img.shields.io/docker/pulls/pkuehnel/teslasolarchargersolaxplugin)](https://hub.docker.com/r/pkuehnel/teslasolarchargersolaxplugin) -To use the Solax plugin, just add these lines to the bottom of your `docker-compose.yml`. Note: You have to specify your solar system's IP address and password. +To use the Solax plugin, just add these lines before the volumes section of your `docker-compose.yml`. Note: You have to specify your solar system's IP address and password. ```yaml solaxplugin: @@ -663,83 +263,7 @@ You can also copy the complete content from here: ```yaml version: '3.3' - services: - teslamate: - image: teslamate/teslamate:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - - TZ=Europe/Berlin ##You can change your Timezone here - ports: - - 4000:4000 - volumes: - - ./import:/opt/app/import - cap_drop: - - all - - database: - image: postgres:15 - restart: always - environment: - - POSTGRES_USER=teslamate - - POSTGRES_PASSWORD=secret ##You can change your password here - - POSTGRES_DB=teslamate - volumes: - - teslamate-db:/var/lib/postgresql/data - - grafana: - image: teslamate/grafana:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - ports: - - 3100:3000 - volumes: - - teslamate-grafana-data:/var/lib/grafana - - mosquitto: - image: eclipse-mosquitto:2 - restart: always - command: mosquitto -c /mosquitto-no-auth.conf - #ports: - # - 1883:1883 - volumes: - - mosquitto-conf:/mosquitto/config - - mosquitto-data:/mosquitto/data - - teslamateapi: - image: tobiasehlert/teslamateapi:latest - logging: - driver: "json-file" - options: - max-file: "5" - max-size: "10m" - restart: always - depends_on: - - database - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - TZ=Europe/Berlin ##You can change your Timezone here - - ENABLE_COMMANDS=true - - COMMANDS_ALL=true - - API_TOKEN_DISABLE=true - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - #ports: - # - 8080:8080 - teslasolarcharger: image: pkuehnel/teslasolarcharger:latest container_name: teslasolarcharger @@ -749,8 +273,6 @@ services: max-file: "10" max-size: "100m" restart: always - depends_on: - - teslamateapi environment: # - Serilog__MinimumLevel__Default=Verbose #uncomment this line and recreate container with docker compose up -d for more detailed logs - TZ=Europe/Berlin ##You can change your Timezone here @@ -775,10 +297,6 @@ services: - 7194:80 volumes: - teslamate-db: - teslamate-grafana-data: - mosquitto-conf: - mosquitto-data: teslasolarcharger-configs: ``` @@ -788,78 +306,66 @@ volumes: 1. Move to your above created directory with your `docker-compose.yml`. 1. Start all containers using the command `docker compose up -d`. -1. Use a third-party app to create a new Tesla Token [[Android](https://play.google.com/store/apps/details?id=net.leveugle.teslatokens&hl=en_US&gl=US)] [[iOS](https://apps.apple.com/us/app/tesla-token/id1411393432)] -1. Open your browser, go to `http://your-ip-address:4000` and paste your token and refresh token into the form. -1. Go to `Geo-Fences` and add a Geo-Fence called `Home` at the location you want TeslaSolarCharger to be active. 1. Open `http://your-ip-address:7190` 1. Go to `Base Configuration` (if you are on a mobile device, it is behind the menu button). +1. Generate a Fleet API token +1. Again go to `Base Configuration` +1. Use the map to select your home area. This is the area where TSC will start charging your car based on solar power. +1. Click on `Save` at the bottom of the page. +1. Go to `Car Settings` and reload the page until the message `Restart TSC to add new cars` is displayed. +1. Restart the container with `docker compose restart teslasolarcharger` (you need to be in the directory of your `docker-compose.yml`). +1. Wake up the car by opening a car door. Now the SoC values, car name,... should be displayed on the `Overview` page: +![image](https://github.com/user-attachments/assets/8ba58c08-f66f-4b4a-897b-439b83a8b04a) +1. If there are any messages displayed below your car name, just follow the instructions. + +If you only want to charge based on Spot Price, you are done now. -##### Setting Up Urls to get grid power +##### Setting up solar power values -To let the TeslaSolarCharger know how much power there is to charge the car, you need to add a value in `Grid Power Url`. +To let the TeslaSolarCharger know how much power there is to charge the car, you need to set TSC up to gather the solar values -###### Using vendor specific plugins +###### REST values including vendor specific plugins +To set up a REST API click on `Add new REST source` and fill out the fields. -**Note:** These values will be filled in automatically in a future release. Maybe it is already working, and I just forgot to remove this section ;-) -Depending on your used plugins, you have to paste one of the following URLs to the `Grid Power Url` field: +If you are using a plugin, you need to use the following values: +- SMA Plugin: + - Url: `http://:7192/api/CurrentPower/GetAllValues` (Note: the serial number in the screenshot is optional in case you have multiple SMA EnergyMeter/Homa Managers) + - Node Pattern Type: JSON + - Add a result with the configuration seen in the screenshot +![SMA plugin](https://github.com/user-attachments/assets/2785c050-e51e-404c-8c61-898f72177374) -- SMA Plugin: `http://:7192/api/CurrentPower/GetPower` -**Note:** If you have more than one EnergyMeter/Home Manager 2.0 in your network, you need to add the serial number of the correct device. A grid URL would look like this then: `http://:7192/api/CurrentPower/GetPower?serialNumber=3001231234` - SolarEdge Plugin: - - Grid Power, InverterPower, HomeBatterySoc, Home Battery Power Url: `http://solaredgeplugin/api/CurrentValues/GetCurrentPvValues` + - Url: `http://solaredgeplugin/api/CurrentValues/GetCurrentPvValues` - Set Result types to json and use the following json patterns: - Grid Power: `$.gridPower` - Inverter Power: `$.inverterPower` - Home Battery SoC: `$.homeBatterySoc` - Home Battery Power: `$.homeBatteryPower` - Solax Plugin: - - Grid Power, InverterPower, HomeBatterySoc, Home Battery Power Url: `http://solaxplugin/api/CurrentValues/GetCurrentPvValues` + - Url: `http://solaxplugin/api/CurrentValues/GetCurrentPvValues` - Set Result types to json and use the following json patterns: - Grid Power: `$.gridPower` - Inverter Power: `$.inverterPower` - Home Battery SoC: `$.homeBatterySoc` - Home Battery Power: `$.homeBatteryPower` - The result should look like this: - ![image](https://user-images.githubusercontent.com/35361981/226210694-18e1af38-25e8-43d8-a13d-6671f0d65fbc.png) - - -###### Using the Modbus plugin - -**Warning:** As this plugin keeps an open connection to your inverter, it is highly recommended not to kill this container but always shut it down gracefully. -To use the Modbus plugin, you must create the URL string yourself. The URL looks like this: +![Solax Plugin](https://github.com/user-attachments/assets/d8d3324b-2988-4532-bb56-2ef4a8b4f52e) -```text -http://modbusplugin/api/Modbus/GetInt32Value?unitIdentifier=3&startingAddress=&quantity=&ipAddress=&port=502&factor=&connectDelaySeconds=1&timeoutSeconds=10 -``` - -An example URL with all values filled could look like this: -```text -http://modbusplugin/api/Modbus/GetInt32Value?unitIdentifier=3&startingAddress=30775&quantity=2&ipAddress=192.168.1.28&port=502&factor=1&connectDelaySeconds=1&timeoutSeconds=10 -``` -You can test the result of the URL by pasting it into your browser and replacing `modbusplugin` with `ipOfYourDockerHost:7191`, e.g.: +###### Modbus values -```text -http://192.168.1.50:7191/api/Modbus/GetInt32Value?unitIdentifier=3&startingAddress=30775&quantity=2&ipAddress=192.168.1.28&port=502&factor=1&connectDelaySeconds=1&timeoutSeconds=10 -``` +Fill out the values according to the documentation of your inverter: -What the values mean: - -- `unitIdentifier`: Internal ID of your inverter (in most cases, 3) -- `startingAddress`: Register address of the value you want to extract. You will find this value in the documentation of your inverter. -- `quantity`: Number of registers to read from (for integer values should be 2) -- `ipAddress`: IP Address of your inverter +- `unitIdentifier`: Internal ID of your inverter (in most cases, 3 or 1) +- `Host`: IP Address or hostname of your inverter - `port`: Modbus TCP Port of your inverter (default: 502) -- `factor`: Factor to multiply the resulting value with. The result should be Watt, so if your inverter returns Watt, you can leave 1. If your inverter returns 0.1W, you have to use 10. -- `connectDelaySeconds`: Delay before communication the first time (you should use 1) -- `timeoutSeconds`: Timeout until returning an error if the inverter is not responding (you should use 10) - -For more convenience, you can go to `http://your-ip-address:7191/swagger`. There you can try your values with a user interface. - -###### Using no plugin +- `Connect Delay Milliseconds`: Delay before communicating the first time (you should use 1000) +- `Read Timeout Milliseconds`: Timeout until returning an error if the inverter is not responding (you should use 1000) +- `Address`: Register address of the value you want to extract. +- `Length`: Number of registers to read from (for integer values should be 2) +- `Correction Factor`: Factor to multiply the resulting value with. The result should be Watt, so if your inverter returns Watt, you can leave 1. If your inverter returns 0.1W, you have to use 10. -If you have your own API or your energymeter directly has a REST API, you can also use these to get the grid power. Just insert the `Grid Power Url` Url; if there is a plain integer value, it should work. If your API returns JSON or XML results, you must add the exact path to that specific value. ###### JSON Path @@ -879,7 +385,7 @@ If you have the following JSON result: } ``` -You can use `$.data.value` as `Grid Power Json Pattern`. +You can use `$.data.value` as `Json Pattern`. ###### XML Path @@ -910,7 +416,7 @@ Grid Power: Assuming the `Measurement` node with `Type` `GridPower` is the power your house feeds to the grid, you need the following values in your Base configuration: ```yaml -- CurrentPowerToGridUrl=http://192.168.xxx.xxx/measurements.xml +- Url=http://192.168.xxx.xxx/measurements.xml - CurrentPowerToGridXmlPattern=Device/Measurements/Measurement - CurrentPowerToGridXmlAttributeHeaderName=Type - CurrentPowerToGridXmlAttributeHeaderValue=GridPower @@ -921,15 +427,13 @@ Inverter Power: Assuming the `Measurement` node with `Type` `AC_Power` is the power your inverter is currently feeding, you can use the following values in your Base configuration: ```yaml -- CurrentInverterPowerUrl=http://192.168.xxx.xxx/measurements.xml +- Url=http://192.168.xxx.xxx/measurements.xml - CurrentInverterPowerXmlPattern=Device/Measurements/Measurement - CurrentInverterPowerAttributeHeaderName=Type - CurrentInverterPowerAttributeHeaderValue=AC_Power - CurrentInverterPowerAttributeValueName=Value ``` -**Note:** These values are not needed. They are just used to show additional information. - #### Correction Factors The correction factor is used to *multiply* the input value so that the results correspond with what TeslaSolarCharger expects. @@ -942,12 +446,12 @@ The correction factor is used to *multiply* the input value so that the results You can use the correction factors to scale/ correct these values as appropriate. For example: -- Grid Power input expresses a positive integer as an import and a negative as an export: Select negative for the operator and 1 for the correction factor (this multiplies by -1). -- Inverter Power is expressed as kW instead of W: Select positive for the operator and 1000 for the correction factor (this multiplies by 1000). -- Home Battery expresses its state of charge as an absolute value in kWh: Select positive for the operator and a correction factor of 1/(Full Charge Capacity) e.g. if the battery has a full charge capacity of 100kWh the correction factor is 1/100 or 0.01 +- Grid Power input expresses a positive integer as an import and a negative as an export: Select `Minus` as operator and `1` for the correction factor (this multiplies by -1). +- Inverter Power is expressed as kW instead of W: Select `Plus` for the operator and 1000 for the correction factor (this multiplies by 1000). +- Home Battery expresses its state of charge as an absolute value in kWh: Select `Plus` for the operator and a correction factor of 1/(Full Charge Capacity) e.g. if the battery has a full charge capacity of 100kWh the correction factor is 1/100 or 0.01 #### Install and setup BLE API -To go around Teslas API limitations, you can use Bluetooth (BLE) to control your car. You can do this either by using the same device as your TSC is running on, or by using a separate device. Note: The device needs to be placed near the car. +To go around Teslas API limitations, you can use Bluetooth (BLE) to control your car. You can do this either by using the same device as your TSC is running on, or by using a separate device. Note: The device needs to be placed near the car. Even if it is working when being a few meters away or in different rooms, I can guarantee you, that you will have issues sooner or later. The device needs to be in one room with the car without any walls between them. Confirmed working hardware: * Raspberry Pi Zero 2W (only capable when used as separate device) @@ -986,81 +490,6 @@ You can also copy the complete content from here: version: '3.3' services: - teslamate: - image: teslamate/teslamate:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - - TZ=Europe/Berlin ##You can change your Timezone here - ports: - - 4000:4000 - volumes: - - ./import:/opt/app/import - cap_drop: - - all - - database: - image: postgres:15 - restart: always - environment: - - POSTGRES_USER=teslamate - - POSTGRES_PASSWORD=secret ##You can change your password here - - POSTGRES_DB=teslamate - volumes: - - teslamate-db:/var/lib/postgresql/data - - grafana: - image: teslamate/grafana:latest - restart: always - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - ports: - - 3100:3000 - volumes: - - teslamate-grafana-data:/var/lib/grafana - - mosquitto: - image: eclipse-mosquitto:2 - restart: always - command: mosquitto -c /mosquitto-no-auth.conf - #ports: - # - 1883:1883 - volumes: - - mosquitto-conf:/mosquitto/config - - mosquitto-data:/mosquitto/data - - teslamateapi: - image: tobiasehlert/teslamateapi:latest - logging: - driver: "json-file" - options: - max-file: "5" - max-size: "10m" - restart: always - depends_on: - - database - environment: - - DATABASE_USER=teslamate - - DATABASE_PASS=secret ##You can change your password here - - DATABASE_NAME=teslamate - - DATABASE_HOST=database - - MQTT_HOST=mosquitto - - TZ=Europe/Berlin ##You can change your Timezone here - - ENABLE_COMMANDS=true - - COMMANDS_ALL=true - - API_TOKEN_DISABLE=true - - ENCRYPTION_KEY=supersecret ##You can change your encryption key here - #ports: - # - 8080:8080 - teslasolarcharger: image: pkuehnel/teslasolarcharger:latest container_name: teslasolarcharger @@ -1070,8 +499,6 @@ services: max-file: "10" max-size: "100m" restart: always - depends_on: - - teslamateapi environment: # - Serilog__MinimumLevel__Default=Verbose #uncomment this line and recreate container with docker compose up -d for more detailed logs - TZ=Europe/Berlin ##You can change your Timezone here @@ -1093,10 +520,6 @@ services: - /var/run/dbus:/var/run/dbus volumes: - teslamate-db: - teslamate-grafana-data: - mosquitto-conf: - mosquitto-data: teslasolarcharger-configs: tscbleapi: ``` @@ -1143,19 +566,15 @@ If you set `PowerBuffer` to a value different from `0`, the system uses the valu To configure your home battery, you need to add the following settings: -- URL for getting the state of charge -- URL for getting current charging/discharging power - Home Battery Minimum SoC - Home Battery Charging Power -After setting everything up, your overview page should look like this: - -![image](https://user-images.githubusercontent.com/35361981/183434947-16d13372-09ff-45a7-94a2-8d4043f39f18.png) +As long as your home battery's SoC is below the set value, the configured charging power is reserved for the home battery. E.g. if you set Home Battery Minimum SoC to 80% and Home Battery Charging Power to 5000W TSC lets the home battery charge with 5000W as long as its SoC is below 80%. -**Note:** If your battery is discharging, the power should be displayed in red. If the battery is charging, the power should be displayed in green. If this is the other way around, you must update the `Correction Factor` below your `HomeBatteryPower Url` setting and invert it to a negative number, e.g. `-1.0`. +**Note:** If your battery is discharging, the power should be displayed in red. If the battery is charging, the power should be displayed in green. If this is the other way around, you must update the `Operator` to be `Minus`. ### Telegram integration -In this section you learn how to create the Telegram Bot Key and where you get the Telegram ChannelID from: +In this section, you learn how to create the Telegram Bot Key and where you get the Telegram ChannelID from: - Create a bot by chatting with `BotFather` @@ -1165,6 +584,7 @@ In this section you learn how to create the Telegram Bot Key and where you get t ![newbot](https://user-images.githubusercontent.com/35361981/233468050-b996475a-fe3a-4131-805e-0fe4c60ce603.jpg) +- Click on the link starting with `t.me/` (second line of `BotFarther`'s answer in the chat) and send any message to your newly created bot. The reason for that is, that a chat exists, where TSC can send messages to. - Copy the Bot token as Telegram Bot Key to your TSC ![BotToken](https://user-images.githubusercontent.com/35361981/233468177-620b0c2f-d9fa-46de-9f87-2eb7b6562553.jpg) @@ -1190,8 +610,8 @@ Currently, there are four different charge modes available: 1. **PV only**: Only solar energy is used to charge. You can set a SOC level that should be reached at the specified date and time (if charge every day is enabled, the car charges to that SoC every day, not only once). If solar power is insufficient to reach the set soc level in time, the car starts charging at full speed. Note: To let this work, specify `usable kWh` in the car settings section. 1. **Maximum Power**: The car charges with the maximum available power -1. **Min SoC + PV**: If plugged in, the car starts charging with maximum power until the set Min SoC is reached. After that, only PV Power is used to charge the car. -1. **Spot Price + PV**: You can set a Min Soc, which should be reached at a specific date and time (if charge every day is enabled, the car charges to that SoC every day, not only once). The charge times are then planned to charge at the cheapest possible time. This is especially useful if you have hourly electricity prices like with [Tibber](https://tibber.com/) or [aWATTar](https://www.awattar.de/). Note: The car will still charge based on Solar energy if available, and you need to enable `Use Spot Price` in the Charge Prices settings for correct charge price calculation. +1. **Min SoC + PV**: If plugged in, the car starts charging with maximum power until the set Min SoC is reached. Thereafter, only PV Power is used to charge the car. +1. **Spot Price + PV**: You can set a Min Soc, which should be reached at a specific date and time (if charge every day is enabled, the car charges to that SoC every day, not only once). The charge times are then planned to charge at the cheapest possible time. This is especially useful if you have hourly electricity prices, like with [Tibber](https://tibber.com/) or [aWATTar](https://www.awattar.de/). Note: The car will still charge based on Solar energy if available, and you need to enable `Use Spot Price` in the Charge Prices settings for correct charge price calculation. 1. **TSC Disabled**: TSC leaves this car as is and does not update the charging speed etc. ## Generate logfiles diff --git a/TeslaSolarCharger.Model/Contracts/IDbConnectionStringHelper.cs b/TeslaSolarCharger.Model/Contracts/IDbConnectionStringHelper.cs index 2ce1907b8..6677e9c30 100644 --- a/TeslaSolarCharger.Model/Contracts/IDbConnectionStringHelper.cs +++ b/TeslaSolarCharger.Model/Contracts/IDbConnectionStringHelper.cs @@ -2,6 +2,6 @@ public interface IDbConnectionStringHelper { - string GetTeslaMateConnectionString(); + string? GetTeslaMateConnectionString(); string GetTeslaSolarChargerDbPath(); } \ No newline at end of file diff --git a/TeslaSolarCharger.Model/Contracts/ITeslaSolarChargerContext.cs b/TeslaSolarCharger.Model/Contracts/ITeslaSolarChargerContext.cs index d55c3d00a..d0f868a7a 100644 --- a/TeslaSolarCharger.Model/Contracts/ITeslaSolarChargerContext.cs +++ b/TeslaSolarCharger.Model/Contracts/ITeslaSolarChargerContext.cs @@ -28,5 +28,6 @@ public interface ITeslaSolarChargerContext DbSet MqttConfigurations { get; set; } DbSet MqttResultConfigurations { get; set; } DbSet BackendNotifications { get; set; } + DbSet LoggedErrors { get; set; } void RejectChanges(); } diff --git a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/Car.cs b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/Car.cs index 9555ce231..5ebdff73a 100644 --- a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/Car.cs +++ b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/Car.cs @@ -1,4 +1,3 @@ -using TeslaSolarCharger.Model.Enums; using TeslaSolarCharger.Shared.Enums; namespace TeslaSolarCharger.Model.Entities.TeslaSolarCharger; @@ -9,12 +8,13 @@ public class Car public int? TeslaMateCarId { get; set; } public string? Name { get; set; } public string? Vin { get; set; } - public TeslaCarFleetApiState TeslaFleetApiState { get; set; } = TeslaCarFleetApiState.NotConfigured; + public TeslaCarFleetApiState? TeslaFleetApiState { get; set; } public ChargeMode ChargeMode { get; set; } public int MinimumSoc { get; set; } public DateTime LatestTimeToReachSoC { get; set; } public bool IgnoreLatestTimeToReachSocDate { get; set; } + public bool IgnoreLatestTimeToReachSocDateOnWeekend { get; set; } public int MaximumAmpere { get; set; } @@ -23,7 +23,6 @@ public class Car public int UsableEnergy { get; set; } public bool? ShouldBeManaged { get; set; } - public bool? ShouldSetChargeStartTimes { get; set; } public int ChargingPriority { get; set; } @@ -41,10 +40,23 @@ public class Car public double? Longitude { get; set; } public CarStateEnum? State { get; set; } public bool VehicleCommandProtocolRequired { get; set; } - public DateTime? RateLimitedUntil { get; set; } + public DateTime? VehicleRateLimitedUntil { get; set; } + public DateTime? VehicleDataRateLimitedUntil { get; set; } + public DateTime? CommandsRateLimitedUntil { get; set; } + public DateTime? WakeUpRateLimitedUntil { get; set; } + public DateTime? ChargingCommandsRateLimitedUntil { get; set; } public bool UseBle { get; set; } + public bool UseBleForWakeUp { get; set; } public int ApiRefreshIntervalSeconds { get; set; } public string? BleApiBaseUrl { get; set; } + public string? WakeUpCalls { get; set; } + public string? VehicleDataCalls { get; set; } + public string? VehicleCalls { get; set; } + public string? ChargeStartCalls { get; set; } + public string? ChargeStopCalls { get; set; } + public string? SetChargingAmpsCall { get; set; } + public string? OtherCommandCalls { get; set; } + public List ChargingProcesses { get; set; } = new List(); } diff --git a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ChargingDetail.cs b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ChargingDetail.cs index 48a172e34..2bae317b0 100644 --- a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ChargingDetail.cs +++ b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ChargingDetail.cs @@ -7,6 +7,7 @@ public class ChargingDetail public int SolarPower { get; set; } public int HomeBatteryPower { get; set; } public int GridPower { get; set; } + public int? ChargerVoltage { get; set; } public int ChargingProcessId { get; set; } diff --git a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/Error.cs b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/Error.cs new file mode 100644 index 000000000..1bfadc992 --- /dev/null +++ b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/Error.cs @@ -0,0 +1,19 @@ +namespace TeslaSolarCharger.Model.Entities.TeslaSolarCharger; + +public class LoggedError +{ + public int Id { get; set; } + public DateTime StartTimeStamp { get; set; } + public DateTime? EndTimeStamp { get; set; } + public List FurtherOccurrences { get; set; } = new(); + public string IssueKey { get; set; } + public string Headline {get; set; } + public string? Vin { get; set; } + public string Source { get; set; } + public string MethodName { get; set; } + public string Message { get; set; } + public string? StackTrace { get; set; } + public DateTime? DismissedAt { get; set; } + public bool TelegramNotificationSent { get; set; } + public bool TelegramResolvedMessageSent { get; set; } +} diff --git a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ModbusConfiguration.cs b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ModbusConfiguration.cs index f2d629f36..b499ec677 100644 --- a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ModbusConfiguration.cs +++ b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ModbusConfiguration.cs @@ -1,5 +1,4 @@ -using TeslaSolarCharger.Model.BaseClasses; -using TeslaSolarCharger.Shared.Enums; +using TeslaSolarCharger.Shared.Enums; namespace TeslaSolarCharger.Model.Entities.TeslaSolarCharger; diff --git a/TeslaSolarCharger.Model/EntityFramework/DbConnectionStringHelper.cs b/TeslaSolarCharger.Model/EntityFramework/DbConnectionStringHelper.cs index 26278b9f3..ff99869fe 100644 --- a/TeslaSolarCharger.Model/EntityFramework/DbConnectionStringHelper.cs +++ b/TeslaSolarCharger.Model/EntityFramework/DbConnectionStringHelper.cs @@ -15,7 +15,7 @@ public DbConnectionStringHelper(ILogger logger, IConfi _configurationWrapper = configurationWrapper; } - public string GetTeslaMateConnectionString() + public string? GetTeslaMateConnectionString() { _logger.LogTrace("{method}()", nameof(GetTeslaMateConnectionString)); var server = _configurationWrapper.TeslaMateDbServer(); @@ -23,6 +23,14 @@ public string GetTeslaMateConnectionString() var databaseName = _configurationWrapper.TeslaMateDbDatabaseName(); var username = _configurationWrapper.TeslaMateDbUser(); var password = _configurationWrapper.TeslaMateDbPassword(); + if (string.IsNullOrEmpty(server) + || port == default + || string.IsNullOrEmpty(databaseName) + || string.IsNullOrEmpty(username) + || string.IsNullOrEmpty(password)) + { + return null; + } var connectionString = $"Host={server};Port={port};Database={databaseName};Username={username};Password={password}"; _logger.LogTrace("ConnectionString: {connectionString}", connectionString); return connectionString; diff --git a/TeslaSolarCharger.Model/EntityFramework/TeslaSolarChargerContext.cs b/TeslaSolarCharger.Model/EntityFramework/TeslaSolarChargerContext.cs index 4149fe90b..5f3d1c253 100644 --- a/TeslaSolarCharger.Model/EntityFramework/TeslaSolarChargerContext.cs +++ b/TeslaSolarCharger.Model/EntityFramework/TeslaSolarChargerContext.cs @@ -1,5 +1,7 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Newtonsoft.Json; using TeslaSolarCharger.Model.Contracts; using TeslaSolarCharger.Model.Converters; using TeslaSolarCharger.Model.Entities.TeslaSolarCharger; @@ -27,6 +29,7 @@ public class TeslaSolarChargerContext : DbContext, ITeslaSolarChargerContext public DbSet MqttConfigurations { get; set; } = null!; public DbSet MqttResultConfigurations { get; set; } = null!; public DbSet BackendNotifications { get; set; } = null!; + public DbSet LoggedErrors { get; set; } = null!; // ReSharper disable once UnassignedGetOnlyAutoProperty public string DbPath { get; } @@ -107,6 +110,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .Property(c => c.ApiRefreshIntervalSeconds) .HasDefaultValue(500); + + var timeListToString = new ValueConverter, string?>( + v => JsonConvert.SerializeObject(v), + v => v == null ? new() : JsonConvert.DeserializeObject>(v) ?? new List() + ); + + var valueComparer = new ValueComparer>( + (c1, c2) => c2 != null && c1 != null && c1.SequenceEqual(c2), // Determines equality + c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), // Calculates hash code + c => c.ToList() // Makes a snapshot copy + ); + + modelBuilder.Entity() + .Property(e => e.FurtherOccurrences) + .HasConversion(timeListToString) + .Metadata.SetValueComparer(valueComparer); + } #pragma warning disable CS8618 diff --git a/TeslaSolarCharger.Model/Migrations/20240606191531_AddIgnoreLatestTimeToReachSocDateOnWeekend.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240606191531_AddIgnoreLatestTimeToReachSocDateOnWeekend.Designer.cs new file mode 100644 index 000000000..c15d2274e --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240606191531_AddIgnoreLatestTimeToReachSocDateOnWeekend.Designer.cs @@ -0,0 +1,717 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240606191531_AddIgnoreLatestTimeToReachSocDateOnWeekend")] + partial class AddIgnoreLatestTimeToReachSocDateOnWeekend + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDateOnWeekend") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("ShouldSetChargeStartTimes") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240606191531_AddIgnoreLatestTimeToReachSocDateOnWeekend.cs b/TeslaSolarCharger.Model/Migrations/20240606191531_AddIgnoreLatestTimeToReachSocDateOnWeekend.cs new file mode 100644 index 000000000..911b2acf9 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240606191531_AddIgnoreLatestTimeToReachSocDateOnWeekend.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class AddIgnoreLatestTimeToReachSocDateOnWeekend : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IgnoreLatestTimeToReachSocDateOnWeekend", + table: "Cars", + type: "INTEGER", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IgnoreLatestTimeToReachSocDateOnWeekend", + table: "Cars"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240709105336_SplitRateLimitedInfoIntoMultipleFields.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240709105336_SplitRateLimitedInfoIntoMultipleFields.Designer.cs new file mode 100644 index 000000000..f92a4600d --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240709105336_SplitRateLimitedInfoIntoMultipleFields.Designer.cs @@ -0,0 +1,786 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240709105336_SplitRateLimitedInfoIntoMultipleFields")] + partial class SplitRateLimitedInfoIntoMultipleFields + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("ShouldSetChargeStartTimes") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240709105336_SplitRateLimitedInfoIntoMultipleFields.cs b/TeslaSolarCharger.Model/Migrations/20240709105336_SplitRateLimitedInfoIntoMultipleFields.cs new file mode 100644 index 000000000..9e571e279 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240709105336_SplitRateLimitedInfoIntoMultipleFields.cs @@ -0,0 +1,69 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class SplitRateLimitedInfoIntoMultipleFields : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "RateLimitedUntil", + table: "Cars", + newName: "WakeUpRateLimitedUntil"); + + migrationBuilder.AddColumn( + name: "ChargingCommandsRateLimitedUntil", + table: "Cars", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "CommandsRateLimitedUntil", + table: "Cars", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "VehicleDataRateLimitedUntil", + table: "Cars", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "VehicleRateLimitedUntil", + table: "Cars", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ChargingCommandsRateLimitedUntil", + table: "Cars"); + + migrationBuilder.DropColumn( + name: "CommandsRateLimitedUntil", + table: "Cars"); + + migrationBuilder.DropColumn( + name: "VehicleDataRateLimitedUntil", + table: "Cars"); + + migrationBuilder.DropColumn( + name: "VehicleRateLimitedUntil", + table: "Cars"); + + migrationBuilder.RenameColumn( + name: "WakeUpRateLimitedUntil", + table: "Cars", + newName: "RateLimitedUntil"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240710132342_CachableCallLists.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240710132342_CachableCallLists.Designer.cs new file mode 100644 index 000000000..6f43c2292 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240710132342_CachableCallLists.Designer.cs @@ -0,0 +1,807 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240710132342_CachableCallLists")] + partial class CachableCallLists + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("SetChargingAmpsCall") + .HasColumnType("TEXT"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("ShouldSetChargeStartTimes") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240710132342_CachableCallLists.cs b/TeslaSolarCharger.Model/Migrations/20240710132342_CachableCallLists.cs new file mode 100644 index 000000000..0f3b8905e --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240710132342_CachableCallLists.cs @@ -0,0 +1,88 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class CachableCallLists : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ChargeStartCalls", + table: "Cars", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "ChargeStopCalls", + table: "Cars", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "OtherCommandCalls", + table: "Cars", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "SetChargingAmpsCall", + table: "Cars", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "VehicleCalls", + table: "Cars", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "VehicleDataCalls", + table: "Cars", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "WakeUpCalls", + table: "Cars", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ChargeStartCalls", + table: "Cars"); + + migrationBuilder.DropColumn( + name: "ChargeStopCalls", + table: "Cars"); + + migrationBuilder.DropColumn( + name: "OtherCommandCalls", + table: "Cars"); + + migrationBuilder.DropColumn( + name: "SetChargingAmpsCall", + table: "Cars"); + + migrationBuilder.DropColumn( + name: "VehicleCalls", + table: "Cars"); + + migrationBuilder.DropColumn( + name: "VehicleDataCalls", + table: "Cars"); + + migrationBuilder.DropColumn( + name: "WakeUpCalls", + table: "Cars"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240710195046_UseWakeForBle.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240710195046_UseWakeForBle.Designer.cs new file mode 100644 index 000000000..29068e815 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240710195046_UseWakeForBle.Designer.cs @@ -0,0 +1,810 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240710195046_UseWakeForBle")] + partial class UseWakeForBle + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("SetChargingAmpsCall") + .HasColumnType("TEXT"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("ShouldSetChargeStartTimes") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("UseBleForWakeUp") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240710195046_UseWakeForBle.cs b/TeslaSolarCharger.Model/Migrations/20240710195046_UseWakeForBle.cs new file mode 100644 index 000000000..6d3c43a8b --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240710195046_UseWakeForBle.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class UseWakeForBle : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UseBleForWakeUp", + table: "Cars", + type: "INTEGER", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UseBleForWakeUp", + table: "Cars"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240820110818_RemoveShouldSetChargeStartTimes.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240820110818_RemoveShouldSetChargeStartTimes.Designer.cs new file mode 100644 index 000000000..02ee71e9a --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240820110818_RemoveShouldSetChargeStartTimes.Designer.cs @@ -0,0 +1,810 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240820110818_RemoveShouldSetChargeStartTimes")] + partial class RemoveShouldSetChargeStartTimes + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDateOnWeekend") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("SetChargingAmpsCall") + .HasColumnType("TEXT"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("UseBleForWakeUp") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240820110818_RemoveShouldSetChargeStartTimes.cs b/TeslaSolarCharger.Model/Migrations/20240820110818_RemoveShouldSetChargeStartTimes.cs new file mode 100644 index 000000000..e9762398a --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240820110818_RemoveShouldSetChargeStartTimes.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class RemoveShouldSetChargeStartTimes : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ShouldSetChargeStartTimes", + table: "Cars"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ShouldSetChargeStartTimes", + table: "Cars", + type: "INTEGER", + nullable: true); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240821101732_CalculateAverageVoltageWithoutTeslaMate.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240821101732_CalculateAverageVoltageWithoutTeslaMate.Designer.cs new file mode 100644 index 000000000..3cceb9f8f --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240821101732_CalculateAverageVoltageWithoutTeslaMate.Designer.cs @@ -0,0 +1,813 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240821101732_CalculateAverageVoltageWithoutTeslaMate")] + partial class CalculateAverageVoltageWithoutTeslaMate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDateOnWeekend") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("SetChargingAmpsCall") + .HasColumnType("TEXT"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("UseBleForWakeUp") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240821101732_CalculateAverageVoltageWithoutTeslaMate.cs b/TeslaSolarCharger.Model/Migrations/20240821101732_CalculateAverageVoltageWithoutTeslaMate.cs new file mode 100644 index 000000000..018f835c0 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240821101732_CalculateAverageVoltageWithoutTeslaMate.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class CalculateAverageVoltageWithoutTeslaMate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ChargerVoltage", + table: "ChargingDetails", + type: "INTEGER", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ChargerVoltage", + table: "ChargingDetails"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240829220344_AddLoggedErrors.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240829220344_AddLoggedErrors.Designer.cs new file mode 100644 index 000000000..60ff6923d --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240829220344_AddLoggedErrors.Designer.cs @@ -0,0 +1,843 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240829220344_AddLoggedErrors")] + partial class AddLoggedErrors + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDateOnWeekend") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("SetChargingAmpsCall") + .HasColumnType("TEXT"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("UseBleForWakeUp") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.LoggedError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndTimeStamp") + .HasColumnType("TEXT"); + + b.Property("IssueKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StartTimeStamp") + .HasColumnType("TEXT"); + + b.Property("TelegramNotificationSent") + .HasColumnType("INTEGER"); + + b.Property("TelegramResolvedMessageSent") + .HasColumnType("INTEGER"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LoggedErrors"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240829220344_AddLoggedErrors.cs b/TeslaSolarCharger.Model/Migrations/20240829220344_AddLoggedErrors.cs new file mode 100644 index 000000000..77dda14f6 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240829220344_AddLoggedErrors.cs @@ -0,0 +1,40 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class AddLoggedErrors : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "LoggedErrors", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + StartTimeStamp = table.Column(type: "TEXT", nullable: false), + EndTimeStamp = table.Column(type: "TEXT", nullable: true), + IssueKey = table.Column(type: "TEXT", nullable: false), + Vin = table.Column(type: "TEXT", nullable: true), + TelegramNotificationSent = table.Column(type: "INTEGER", nullable: false), + TelegramResolvedMessageSent = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LoggedErrors", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "LoggedErrors"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240908212048_AddAddtionalLoggedErrorColumns.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240908212048_AddAddtionalLoggedErrorColumns.Designer.cs new file mode 100644 index 000000000..87ccaa2d0 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240908212048_AddAddtionalLoggedErrorColumns.Designer.cs @@ -0,0 +1,858 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240908212048_AddAddtionalLoggedErrorColumns")] + partial class AddAddtionalLoggedErrorColumns + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDateOnWeekend") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("SetChargingAmpsCall") + .HasColumnType("TEXT"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("UseBleForWakeUp") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.LoggedError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndTimeStamp") + .HasColumnType("TEXT"); + + b.Property("IssueKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MethodName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Source") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StackTrace") + .HasColumnType("TEXT"); + + b.Property("StartTimeStamp") + .HasColumnType("TEXT"); + + b.Property("TelegramNotificationSent") + .HasColumnType("INTEGER"); + + b.Property("TelegramResolvedMessageSent") + .HasColumnType("INTEGER"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LoggedErrors"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240908212048_AddAddtionalLoggedErrorColumns.cs b/TeslaSolarCharger.Model/Migrations/20240908212048_AddAddtionalLoggedErrorColumns.cs new file mode 100644 index 000000000..5abf704a1 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240908212048_AddAddtionalLoggedErrorColumns.cs @@ -0,0 +1,61 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class AddAddtionalLoggedErrorColumns : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Message", + table: "LoggedErrors", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "MethodName", + table: "LoggedErrors", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "Source", + table: "LoggedErrors", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "StackTrace", + table: "LoggedErrors", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Message", + table: "LoggedErrors"); + + migrationBuilder.DropColumn( + name: "MethodName", + table: "LoggedErrors"); + + migrationBuilder.DropColumn( + name: "Source", + table: "LoggedErrors"); + + migrationBuilder.DropColumn( + name: "StackTrace", + table: "LoggedErrors"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240917152832_AddFurtherOccurrencesToLoggedErrors.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240917152832_AddFurtherOccurrencesToLoggedErrors.Designer.cs new file mode 100644 index 000000000..735353b52 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240917152832_AddFurtherOccurrencesToLoggedErrors.Designer.cs @@ -0,0 +1,862 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240917152832_AddFurtherOccurrencesToLoggedErrors")] + partial class AddFurtherOccurrencesToLoggedErrors + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDateOnWeekend") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("SetChargingAmpsCall") + .HasColumnType("TEXT"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("UseBleForWakeUp") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.LoggedError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndTimeStamp") + .HasColumnType("TEXT"); + + b.Property("FurtherOccurrences") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IssueKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MethodName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Source") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StackTrace") + .HasColumnType("TEXT"); + + b.Property("StartTimeStamp") + .HasColumnType("TEXT"); + + b.Property("TelegramNotificationSent") + .HasColumnType("INTEGER"); + + b.Property("TelegramResolvedMessageSent") + .HasColumnType("INTEGER"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LoggedErrors"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240917152832_AddFurtherOccurrencesToLoggedErrors.cs b/TeslaSolarCharger.Model/Migrations/20240917152832_AddFurtherOccurrencesToLoggedErrors.cs new file mode 100644 index 000000000..fcbcacdb7 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240917152832_AddFurtherOccurrencesToLoggedErrors.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class AddFurtherOccurrencesToLoggedErrors : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "FurtherOccurrences", + table: "LoggedErrors", + type: "TEXT", + nullable: false, + defaultValue: ""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FurtherOccurrences", + table: "LoggedErrors"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240922122958_AddHeadingToLoggedErrors.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240922122958_AddHeadingToLoggedErrors.Designer.cs new file mode 100644 index 000000000..27f178518 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240922122958_AddHeadingToLoggedErrors.Designer.cs @@ -0,0 +1,866 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240922122958_AddHeadingToLoggedErrors")] + partial class AddHeadingToLoggedErrors + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDateOnWeekend") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("SetChargingAmpsCall") + .HasColumnType("TEXT"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("UseBleForWakeUp") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.LoggedError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndTimeStamp") + .HasColumnType("TEXT"); + + b.Property("FurtherOccurrences") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IssueKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MethodName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Source") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StackTrace") + .HasColumnType("TEXT"); + + b.Property("StartTimeStamp") + .HasColumnType("TEXT"); + + b.Property("TelegramNotificationSent") + .HasColumnType("INTEGER"); + + b.Property("TelegramResolvedMessageSent") + .HasColumnType("INTEGER"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LoggedErrors"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240922122958_AddHeadingToLoggedErrors.cs b/TeslaSolarCharger.Model/Migrations/20240922122958_AddHeadingToLoggedErrors.cs new file mode 100644 index 000000000..cd9bef4e4 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240922122958_AddHeadingToLoggedErrors.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class AddHeadingToLoggedErrors : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Headline", + table: "LoggedErrors", + type: "TEXT", + nullable: false, + defaultValue: ""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Headline", + table: "LoggedErrors"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240923073439_AddLoggedErrorDismissedAt.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240923073439_AddLoggedErrorDismissedAt.Designer.cs new file mode 100644 index 000000000..10d699b0d --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240923073439_AddLoggedErrorDismissedAt.Designer.cs @@ -0,0 +1,869 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240923073439_AddLoggedErrorDismissedAt")] + partial class AddLoggedErrorDismissedAt + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDateOnWeekend") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("SetChargingAmpsCall") + .HasColumnType("TEXT"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("UseBleForWakeUp") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.LoggedError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DismissedAt") + .HasColumnType("TEXT"); + + b.Property("EndTimeStamp") + .HasColumnType("TEXT"); + + b.Property("FurtherOccurrences") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IssueKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MethodName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Source") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StackTrace") + .HasColumnType("TEXT"); + + b.Property("StartTimeStamp") + .HasColumnType("TEXT"); + + b.Property("TelegramNotificationSent") + .HasColumnType("INTEGER"); + + b.Property("TelegramResolvedMessageSent") + .HasColumnType("INTEGER"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LoggedErrors"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240923073439_AddLoggedErrorDismissedAt.cs b/TeslaSolarCharger.Model/Migrations/20240923073439_AddLoggedErrorDismissedAt.cs new file mode 100644 index 000000000..ce59cabd8 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240923073439_AddLoggedErrorDismissedAt.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class AddLoggedErrorDismissedAt : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DismissedAt", + table: "LoggedErrors", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DismissedAt", + table: "LoggedErrors"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240930083938_MakeFleetApiStateNullable.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240930083938_MakeFleetApiStateNullable.Designer.cs new file mode 100644 index 000000000..f9db885ab --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240930083938_MakeFleetApiStateNullable.Designer.cs @@ -0,0 +1,869 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240930083938_MakeFleetApiStateNullable")] + partial class MakeFleetApiStateNullable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.BackendNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BackendIssueId") + .HasColumnType("INTEGER"); + + b.Property("DetailText") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsConfirmed") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("ValidFromDate") + .HasColumnType("TEXT"); + + b.Property("ValidFromVersion") + .HasColumnType("TEXT"); + + b.Property("ValidToDate") + .HasColumnType("TEXT"); + + b.Property("ValidToVersion") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("BackendNotifications"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiRefreshIntervalSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(500); + + b.Property("BleApiBaseUrl") + .HasColumnType("TEXT"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDateOnWeekend") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("SetChargingAmpsCall") + .HasColumnType("TEXT"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("UseBle") + .HasColumnType("INTEGER"); + + b.Property("UseBleForWakeUp") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleCommandProtocolRequired") + .HasColumnType("INTEGER"); + + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("HomeBatteryPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("OldHandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedHomeBatteryEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.LoggedError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DismissedAt") + .HasColumnType("TEXT"); + + b.Property("EndTimeStamp") + .HasColumnType("TEXT"); + + b.Property("FurtherOccurrences") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IssueKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MethodName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Source") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StackTrace") + .HasColumnType("TEXT"); + + b.Property("StartTimeStamp") + .HasColumnType("TEXT"); + + b.Property("TelegramNotificationSent") + .HasColumnType("INTEGER"); + + b.Property("TelegramResolvedMessageSent") + .HasColumnType("INTEGER"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LoggedErrors"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConnectDelayMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("Endianess") + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ReadTimeoutMilliseconds") + .HasColumnType("INTEGER"); + + b.Property("UnitIdentifier") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ModbusConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("INTEGER"); + + b.Property("BitStartIndex") + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("InvertedByModbusResultConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("ModbusConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RegisterType") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InvertedByModbusResultConfigurationId"); + + b.HasIndex("ModbusConfigurationId"); + + b.ToTable("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("MqttConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("MqttConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MqttConfigurationId"); + + b.ToTable("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusResultConfiguration", "InvertedByModbusResultConfiguration") + .WithMany() + .HasForeignKey("InvertedByModbusResultConfigurationId"); + + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", "ModbusConfiguration") + .WithMany("ModbusResultConfigurations") + .HasForeignKey("ModbusConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InvertedByModbusResultConfiguration"); + + b.Navigation("ModbusConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", "MqttConfiguration") + .WithMany("MqttResultConfigurations") + .HasForeignKey("MqttConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MqttConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => + { + b.Navigation("ModbusResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.MqttConfiguration", b => + { + b.Navigation("MqttResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240930083938_MakeFleetApiStateNullable.cs b/TeslaSolarCharger.Model/Migrations/20240930083938_MakeFleetApiStateNullable.cs new file mode 100644 index 000000000..f1fb31ee4 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240930083938_MakeFleetApiStateNullable.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class MakeFleetApiStateNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "TeslaFleetApiState", + table: "Cars", + type: "INTEGER", + nullable: true, + oldClrType: typeof(int), + oldType: "INTEGER"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "TeslaFleetApiState", + table: "Cars", + type: "INTEGER", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "INTEGER", + oldNullable: true); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs b/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs index 322091422..6189c5f2a 100644 --- a/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs +++ b/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs @@ -98,6 +98,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ChargeMode") .HasColumnType("INTEGER"); + b.Property("ChargeStartCalls") + .HasColumnType("TEXT"); + + b.Property("ChargeStopCalls") + .HasColumnType("TEXT"); + b.Property("ChargerActualCurrent") .HasColumnType("INTEGER"); @@ -113,15 +119,24 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ChargerVoltage") .HasColumnType("INTEGER"); + b.Property("ChargingCommandsRateLimitedUntil") + .HasColumnType("TEXT"); + b.Property("ChargingPriority") .HasColumnType("INTEGER"); b.Property("ClimateOn") .HasColumnType("INTEGER"); + b.Property("CommandsRateLimitedUntil") + .HasColumnType("TEXT"); + b.Property("IgnoreLatestTimeToReachSocDate") .HasColumnType("INTEGER"); + b.Property("IgnoreLatestTimeToReachSocDateOnWeekend") + .HasColumnType("INTEGER"); + b.Property("LatestTimeToReachSoC") .HasColumnType("TEXT"); @@ -143,18 +158,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("TEXT"); + b.Property("OtherCommandCalls") + .HasColumnType("TEXT"); + b.Property("PluggedIn") .HasColumnType("INTEGER"); - b.Property("RateLimitedUntil") + b.Property("SetChargingAmpsCall") .HasColumnType("TEXT"); b.Property("ShouldBeManaged") .HasColumnType("INTEGER"); - b.Property("ShouldSetChargeStartTimes") - .HasColumnType("INTEGER"); - b.Property("SoC") .HasColumnType("INTEGER"); @@ -164,7 +179,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("State") .HasColumnType("INTEGER"); - b.Property("TeslaFleetApiState") + b.Property("TeslaFleetApiState") .HasColumnType("INTEGER"); b.Property("TeslaMateCarId") @@ -176,12 +191,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("UseBle") .HasColumnType("INTEGER"); + b.Property("UseBleForWakeUp") + .HasColumnType("INTEGER"); + + b.Property("VehicleCalls") + .HasColumnType("TEXT"); + b.Property("VehicleCommandProtocolRequired") .HasColumnType("INTEGER"); + b.Property("VehicleDataCalls") + .HasColumnType("TEXT"); + + b.Property("VehicleDataRateLimitedUntil") + .HasColumnType("TEXT"); + + b.Property("VehicleRateLimitedUntil") + .HasColumnType("TEXT"); + b.Property("Vin") .HasColumnType("TEXT"); + b.Property("WakeUpCalls") + .HasColumnType("TEXT"); + + b.Property("WakeUpRateLimitedUntil") + .HasColumnType("TEXT"); + b.HasKey("Id"); b.HasIndex("TeslaMateCarId") @@ -233,6 +269,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + b.Property("ChargingProcessId") .HasColumnType("INTEGER"); @@ -321,6 +360,62 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("HandledCharges"); }); + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.LoggedError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DismissedAt") + .HasColumnType("TEXT"); + + b.Property("EndTimeStamp") + .HasColumnType("TEXT"); + + b.Property("FurtherOccurrences") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Headline") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IssueKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MethodName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Source") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StackTrace") + .HasColumnType("TEXT"); + + b.Property("StartTimeStamp") + .HasColumnType("TEXT"); + + b.Property("TelegramNotificationSent") + .HasColumnType("INTEGER"); + + b.Property("TelegramResolvedMessageSent") + .HasColumnType("INTEGER"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LoggedErrors"); + }); + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ModbusConfiguration", b => { b.Property("Id") diff --git a/TeslaSolarCharger.Model/TeslaSolarCharger.Model.csproj b/TeslaSolarCharger.Model/TeslaSolarCharger.Model.csproj index 2b90c45ef..748e332f6 100644 --- a/TeslaSolarCharger.Model/TeslaSolarCharger.Model.csproj +++ b/TeslaSolarCharger.Model/TeslaSolarCharger.Model.csproj @@ -7,16 +7,16 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/TeslaSolarCharger.Services/ServiceCollectionExtensions.cs b/TeslaSolarCharger.Services/ServiceCollectionExtensions.cs index e98777806..442e51604 100644 --- a/TeslaSolarCharger.Services/ServiceCollectionExtensions.cs +++ b/TeslaSolarCharger.Services/ServiceCollectionExtensions.cs @@ -25,6 +25,5 @@ public static IServiceCollection AddServicesDependencies(this IServiceCollection .AddSingleton() .AddTransient() .AddTransient() - .AddTransient() ; } diff --git a/TeslaSolarCharger.Services/Services/CarConfigurationService.cs b/TeslaSolarCharger.Services/Services/CarConfigurationService.cs deleted file mode 100644 index 2549dbfc8..000000000 --- a/TeslaSolarCharger.Services/Services/CarConfigurationService.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using System.Threading.Tasks.Sources; -using TeslaSolarCharger.Model.Contracts; -using TeslaSolarCharger.Model.Entities.TeslaSolarCharger; -using TeslaSolarCharger.Services.Services.Contracts; -using TeslaSolarCharger.Shared.Contracts; -using TeslaSolarCharger.Shared.Enums; - -namespace TeslaSolarCharger.Services.Services; - -public class CarConfigurationService(ILogger logger, - ITeslaSolarChargerContext teslaSolarChargerContext, - ITeslamateContext teslamateContext, - IDateTimeProvider dateTimeProvider) : ICarConfigurationService -{ - public async Task AddAllMissingTeslaMateCars() - { - logger.LogTrace("{method}()", nameof(AddAllMissingTeslaMateCars)); - var teslaMateCars = await teslamateContext.Cars.ToListAsync(); - var teslaSolarChargerCars = await teslaSolarChargerContext.Cars.ToListAsync(); - var highestChargingPriority = 0; - if (teslaSolarChargerCars.Any()) - { - highestChargingPriority = teslaSolarChargerCars.Max(c => c.ChargingPriority); - } - foreach (var teslaMateCar in teslaMateCars) - { - var vin = teslaMateCar.Vin; - if (string.IsNullOrWhiteSpace(vin)) - { - logger.LogWarning("Car with id {id} has no vin", teslaMateCar.Id); - continue; - } - if (teslaSolarChargerContext.Cars.Any(c => c.Vin == vin)) - { - continue; - } - var teslaSolarChargerCar = new Car - { - TeslaMateCarId = teslaMateCar.Id, - Vin = vin, - Name = teslaMateCar.Name, - TeslaFleetApiState = TeslaCarFleetApiState.NotConfigured, - ChargeMode = ChargeMode.PvAndMinSoc, - MinimumSoc = 10, - LatestTimeToReachSoC = dateTimeProvider.UtcNow(), - IgnoreLatestTimeToReachSocDate = false, - MaximumAmpere = 16, - MinimumAmpere = 6, - UsableEnergy = 75, - ShouldBeManaged = true, - ShouldSetChargeStartTimes = false, - ChargingPriority = ++highestChargingPriority, - }; - teslaSolarChargerContext.Cars.Add(teslaSolarChargerCar); - await teslaSolarChargerContext.SaveChangesAsync(); - } - } -} diff --git a/TeslaSolarCharger.Services/Services/Contracts/ICarConfigurationService.cs b/TeslaSolarCharger.Services/Services/Contracts/ICarConfigurationService.cs deleted file mode 100644 index f8781ee74..000000000 --- a/TeslaSolarCharger.Services/Services/Contracts/ICarConfigurationService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace TeslaSolarCharger.Services.Services.Contracts; - -public interface ICarConfigurationService -{ - Task AddAllMissingTeslaMateCars(); -} diff --git a/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs b/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs index a3e3a9302..996b75b57 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs @@ -15,13 +15,21 @@ public async Task GetByteArrayFromHoldingRegisters(byte unitIdentifier, { logger.LogTrace("{method}({unitIdentifier}, {startingAddress}, {quantity})", nameof(GetByteArrayFromHoldingRegisters), unitIdentifier, startingAddress, quantity); await _semaphoreSlim.WaitAsync().ConfigureAwait(false); + using var cts = new CancellationTokenSource((int)readTimeout.TotalMilliseconds); try { ReadTimeout = (int)readTimeout.TotalMilliseconds; logger.LogTrace("ReadTimeout: {ReadTimeout}", ReadTimeout); - var result = await base.ReadHoldingRegistersAsync(unitIdentifier, startingAddress, quantity); + var result = await base.ReadHoldingRegistersAsync(unitIdentifier, startingAddress, quantity, cts.Token); return result.ToArray(); } + catch (OperationCanceledException ex) + { + logger.LogWarning(ex, "Read operation for {unitIdentifier}, {startingAddress}, {quantity} was canceled due to timeout. Disconnecting modbus client.", unitIdentifier, startingAddress, quantity); + Disconnect(); + logger.LogDebug("Modbus Client disconnected."); + throw; + } finally { _semaphoreSlim.Release(); @@ -34,13 +42,21 @@ public async Task GetByteArrayFromInputRegisters(byte unitIdentifier, us { logger.LogTrace("{method}({unitIdentifier}, {startingAddress}, {quantity})", nameof(GetByteArrayFromInputRegisters), unitIdentifier, startingAddress, quantity); await _semaphoreSlim.WaitAsync().ConfigureAwait(false); + using var cts = new CancellationTokenSource((int)readTimeout.TotalMilliseconds); try { ReadTimeout = (int)readTimeout.TotalMilliseconds; logger.LogTrace("ReadTimeout: {ReadTimeout}", ReadTimeout); - var result = await base.ReadInputRegistersAsync(unitIdentifier, startingAddress, quantity); + var result = await base.ReadInputRegistersAsync(unitIdentifier, startingAddress, quantity, cts.Token); return result.ToArray(); } + catch (OperationCanceledException ex) + { + logger.LogWarning(ex, "Read operation for {unitIdentifier}, {startingAddress}, {quantity} was canceled due to timeout. Disconnecting modbus client.", unitIdentifier, startingAddress, quantity); + Disconnect(); + logger.LogDebug("Modbus Client disconnected."); + throw; + } finally { _semaphoreSlim.Release(); diff --git a/TeslaSolarCharger.Services/TeslaSolarCharger.Services.csproj b/TeslaSolarCharger.Services/TeslaSolarCharger.Services.csproj index f4fd8fbd3..2d19ef34f 100644 --- a/TeslaSolarCharger.Services/TeslaSolarCharger.Services.csproj +++ b/TeslaSolarCharger.Services/TeslaSolarCharger.Services.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/TeslaSolarCharger.SharedBackend/Dtos/DtoFleetApiRequest.cs b/TeslaSolarCharger.SharedBackend/Dtos/DtoFleetApiRequest.cs index 383bb4680..c95789e0a 100644 --- a/TeslaSolarCharger.SharedBackend/Dtos/DtoFleetApiRequest.cs +++ b/TeslaSolarCharger.SharedBackend/Dtos/DtoFleetApiRequest.cs @@ -1,8 +1,11 @@ -namespace TeslaSolarCharger.SharedBackend.Dtos; +using TeslaSolarCharger.SharedBackend.Enums; + +namespace TeslaSolarCharger.SharedBackend.Dtos; public class DtoFleetApiRequest { public string RequestUrl { get; set; } public bool NeedsProxy { get; set; } public bool BleCompatible { get; set; } + public TeslaApiRequestType TeslaApiRequestType { get; set; } } diff --git a/TeslaSolarCharger.SharedBackend/Enums/TeslaApiRequestType.cs b/TeslaSolarCharger.SharedBackend/Enums/TeslaApiRequestType.cs new file mode 100644 index 000000000..4faa35ab9 --- /dev/null +++ b/TeslaSolarCharger.SharedBackend/Enums/TeslaApiRequestType.cs @@ -0,0 +1,11 @@ +namespace TeslaSolarCharger.SharedBackend.Enums; + +public enum TeslaApiRequestType +{ + Vehicle, + VehicleData, + Command, + WakeUp, + Charging, + Other, +} diff --git a/TeslaSolarCharger.SharedBackend/Extensions/FinExtensions.cs b/TeslaSolarCharger.SharedBackend/Extensions/FinExtensions.cs new file mode 100644 index 000000000..930c50561 --- /dev/null +++ b/TeslaSolarCharger.SharedBackend/Extensions/FinExtensions.cs @@ -0,0 +1,39 @@ +using LanguageExt; +using Microsoft.AspNetCore.Mvc; + +namespace TeslaSolarCharger.SharedBackend.Extensions; + +public static class FinExtensions +{ + public static IActionResult ToOk(this Fin result) + { + return result.Match( + Succ: succ => new OkObjectResult(succ), + Fail: err => + { + if (!err.IsExceptional) + { + return new ObjectResult(new ProblemDetails() { Detail = err.Message, Status = 500, }); + } + + var exception = err.ToException(); + if (exception is HttpRequestException httpRequestException) + { + var problemDetails = new ProblemDetails() + { + Detail = $"Error while calling API from backend: {httpRequestException.Message}", + Status = (int?)httpRequestException.StatusCode, + }; + return new ObjectResult(problemDetails) + { + StatusCode = problemDetails.Status, + }; + } + return new ObjectResult(new ProblemDetails() + { + Detail = err.Message, + Status = 500, + }); + }); + } +} diff --git a/TeslaSolarCharger.SharedBackend/TeslaSolarCharger.SharedBackend.csproj b/TeslaSolarCharger.SharedBackend/TeslaSolarCharger.SharedBackend.csproj index f9117b868..036bf7760 100644 --- a/TeslaSolarCharger.SharedBackend/TeslaSolarCharger.SharedBackend.csproj +++ b/TeslaSolarCharger.SharedBackend/TeslaSolarCharger.SharedBackend.csproj @@ -14,7 +14,9 @@ + + diff --git a/TeslaSolarCharger.Tests/Data/DataGenerator.cs b/TeslaSolarCharger.Tests/Data/DataGenerator.cs index 47664d37e..63282ac14 100644 --- a/TeslaSolarCharger.Tests/Data/DataGenerator.cs +++ b/TeslaSolarCharger.Tests/Data/DataGenerator.cs @@ -1,10 +1,12 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.VisualBasic; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using TeslaSolarCharger.Model.Entities.TeslaSolarCharger; using TeslaSolarCharger.Model.EntityFramework; +using TeslaSolarCharger.Shared.Enums; using TeslaSolarCharger.SharedModel.Enums; namespace TeslaSolarCharger.Tests.Data; @@ -81,4 +83,66 @@ public static TeslaSolarChargerContext InitRestValueConfigurations(this TeslaSol }); return context; } + + public static TeslaSolarChargerContext InitLoggedErrors(this TeslaSolarChargerContext context) + { + context.LoggedErrors.AddRange(new List() + { + new() + { + Id = -1, + Headline = "Not Hidden Test", + IssueKey = "CarStateUnknown", + StartTimeStamp = new(2023, 1, 22, 17, 0, 0, DateTimeKind.Utc), + FurtherOccurrences = [new(2023, 1, 22, 17, 1, 0, DateTimeKind.Utc)], + Message = "Test Error Message", + Vin = "1234567890", + Source = nameof(LoggedError.Source), + MethodName = nameof(LoggedError.MethodName), + StackTrace = "Test Stack Trace", + }, + new() + { + Id = -2, + Headline = "Not Hidden Test due to dismissed in before last occurrence", + IssueKey = "CarStateUnknown", + StartTimeStamp = new(2023, 1, 22, 17, 0, 0, DateTimeKind.Utc), + FurtherOccurrences = [new(2023, 1, 22, 17, 1, 0, DateTimeKind.Utc)], + Message = "Test Error Message", + Vin = "1234567890", + Source = nameof(LoggedError.Source), + MethodName = nameof(LoggedError.MethodName), + StackTrace = "Test Stack Trace", + DismissedAt = new DateTime(2023, 1, 22, 17, 0, 30, DateTimeKind.Utc), + }, + new() + { + Id = -3, + Headline = "Not Enough Occurrences Test", + IssueKey = "CarStateUnknown", + StartTimeStamp = new(2023, 1, 22, 17, 0, 0, DateTimeKind.Utc), + FurtherOccurrences = [], + Message = "Test Error Message", + Vin = "1234567890", + Source = nameof(LoggedError.Source), + MethodName = nameof(LoggedError.MethodName), + StackTrace = "Test Stack Trace", + }, + new() + { + Id = -4, + Headline = "Dismissed Test", + IssueKey = "CarStateUnknown", + StartTimeStamp = new(2023, 1, 22, 17, 0, 0, DateTimeKind.Utc), + FurtherOccurrences = [new(2023, 1, 22, 17, 1, 0, DateTimeKind.Utc)], + Message = "Test Error Message", + Vin = "1234567890", + Source = nameof(LoggedError.Source), + MethodName = nameof(LoggedError.MethodName), + StackTrace = "Test Stack Trace", + DismissedAt = new DateTime(2023, 1, 22, 17, 1, 30, DateTimeKind.Utc), + }, + }); + return context; + } } diff --git a/TeslaSolarCharger.Tests/Helper/BaseConfigurationConverter.cs b/TeslaSolarCharger.Tests/Helper/BaseConfigurationConverter.cs deleted file mode 100644 index d9873c22a..000000000 --- a/TeslaSolarCharger.Tests/Helper/BaseConfigurationConverter.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using Newtonsoft.Json; -using TeslaSolarCharger.Shared.Dtos.BaseConfiguration.OldVersions.V0._1; -using Xunit; -using Xunit.Abstractions; - -namespace TeslaSolarCharger.Tests.Helper; - -public class BaseConfigurationConverter : TestBase -{ - public BaseConfigurationConverter(ITestOutputHelper outputHelper) - : base(outputHelper) - { - } - - [Fact] - public void Can_Detect_Missing_Version() - { - var converter = Mock.Create(); - const string baseConfigJsonString = "{\"LastEditDateTime\":\"2022-07-21T13:44:22.9562369Z\",\"CurrentPowerToGridUrl\":\"http://192.168.1.50:5007/api/ChargingLog/GetCurrentGridPower\",\"CurrentInverterPowerUrl\":\"http://modbusplugin/api/Modbus/GetValue?unitIdentifier=3&startingAddress=30775&quantity=2&ipAddress=192.168.1.28&port=502&factor=1&connectDelaySeconds=0&timeoutSeconds=1&minimumResult=0\",\"TeslaMateApiBaseUrl\":\"http://teslamateapi:8080\",\"UpdateIntervalSeconds\":30,\"PvValueUpdateIntervalSeconds\":1,\"CarPriorities\":\"1|2\",\"GeoFence\":\"Zu Hause\",\"MinutesUntilSwitchOn\":5,\"MinutesUntilSwitchOff\":5,\"PowerBuffer\":0,\"CurrentPowerToGridJsonPattern\":null,\"CurrentPowerToGridInvertValue\":false,\"CurrentInverterPowerJsonPattern\":null,\"TelegramBotKey\":\"\",\"TelegramChannelId\":\"\",\"TeslaMateDbServer\":\"database\",\"TeslaMateDbPort\":5432,\"TeslaMateDbDatabaseName\":\"teslamate\",\"TeslaMateDbUser\":\"teslamate\",\"TeslaMateDbPassword\":\"secret\",\"MqqtClientId\":\"TeslaSolarCharger\",\"MosquitoServer\":\"mosquitto\",\"CurrentPowerToGridXmlPattern\":null,\"CurrentPowerToGridXmlAttributeHeaderName\":null,\"CurrentPowerToGridXmlAttributeHeaderValue\":null,\"CurrentPowerToGridXmlAttributeValueName\":null,\"CurrentInverterPowerXmlPattern\":null,\"CurrentInverterPowerXmlAttributeHeaderName\":null,\"CurrentInverterPowerXmlAttributeHeaderValue\":null,\"CurrentInverterPowerXmlAttributeValueName\":null}"; - - var version = converter.GetVersionFromBaseConfigurationJsonString(baseConfigJsonString); - - Assert.Equal(new Version(0, 1), version); - } - - [Fact] - public void Can_Move_BaseConfig_ValuesV0_1ToV1_0() - { - var converter = Mock.Create(); - const string baseConfigJsonString = "{\"LastEditDateTime\":\"2022-07-21T13:44:22.9562369Z\",\"CurrentPowerToGridUrl\":\"http://192.168.1.50:5007/api/ChargingLog/GetCurrentGridPower\",\"CurrentInverterPowerUrl\":\"http://modbusplugin/api/Modbus/GetValue?unitIdentifier=3&startingAddress=30775&quantity=2&ipAddress=192.168.1.28&port=502&factor=1&connectDelaySeconds=0&timeoutSeconds=1&minimumResult=0\",\"TeslaMateApiBaseUrl\":\"http://teslamateapi:8080\",\"UpdateIntervalSeconds\":30,\"PvValueUpdateIntervalSeconds\":1,\"CarPriorities\":\"1|2\",\"GeoFence\":\"Zu Hause\",\"MinutesUntilSwitchOn\":5,\"MinutesUntilSwitchOff\":5,\"PowerBuffer\":0,\"CurrentPowerToGridJsonPattern\":null,\"CurrentPowerToGridInvertValue\":false,\"CurrentInverterPowerJsonPattern\":null,\"TelegramBotKey\":\"\",\"TelegramChannelId\":\"\",\"TeslaMateDbServer\":\"database\",\"TeslaMateDbPort\":5432,\"TeslaMateDbDatabaseName\":\"teslamate\",\"TeslaMateDbUser\":\"teslamate\",\"TeslaMateDbPassword\":\"secret\",\"MqqtClientId\":\"TeslaSolarCharger\",\"MosquitoServer\":\"mosquitto\",\"CurrentPowerToGridXmlPattern\":null,\"CurrentPowerToGridXmlAttributeHeaderName\":null,\"CurrentPowerToGridXmlAttributeHeaderValue\":null,\"CurrentPowerToGridXmlAttributeValueName\":null,\"CurrentInverterPowerXmlPattern\":null,\"CurrentInverterPowerXmlAttributeHeaderName\":null,\"CurrentInverterPowerXmlAttributeHeaderValue\":null,\"CurrentInverterPowerXmlAttributeValueName\":null}"; - - var oldBaseConfig = JsonConvert.DeserializeObject(baseConfigJsonString) ?? throw new InvalidOperationException(); - - var newBaseConfig = converter.ConvertV0_1ToV1_0(oldBaseConfig); - - Assert.Equal(oldBaseConfig.CurrentInverterPowerUrl, newBaseConfig.CurrentInverterPowerUrl); - Assert.Equal(oldBaseConfig.UpdateIntervalSeconds, newBaseConfig.UpdateIntervalSeconds); - Assert.Equal(oldBaseConfig.CurrentPowerToGridJsonPattern, newBaseConfig.CurrentPowerToGridJsonPattern); - Assert.Equal(1, newBaseConfig.CurrentPowerToGridCorrectionFactor); - } - - [Fact] - public void Can_Convert_BaseConfigValuesV0_1ToV1_0_InvertGridValueToFactor() - { - var converter = Mock.Create(); - const string baseConfigJsonString = "{\"LastEditDateTime\":\"2022-07-21T13:44:22.9562369Z\",\"CurrentPowerToGridUrl\":\"http://192.168.1.50:5007/api/ChargingLog/GetCurrentGridPower\",\"CurrentInverterPowerUrl\":\"http://modbusplugin/api/Modbus/GetValue?unitIdentifier=3&startingAddress=30775&quantity=2&ipAddress=192.168.1.28&port=502&factor=1&connectDelaySeconds=0&timeoutSeconds=1&minimumResult=0\",\"TeslaMateApiBaseUrl\":\"http://teslamateapi:8080\",\"UpdateIntervalSeconds\":30,\"PvValueUpdateIntervalSeconds\":1,\"CarPriorities\":\"1|2\",\"GeoFence\":\"Zu Hause\",\"MinutesUntilSwitchOn\":5,\"MinutesUntilSwitchOff\":5,\"PowerBuffer\":0,\"CurrentPowerToGridJsonPattern\":null,\"CurrentPowerToGridInvertValue\":true,\"CurrentInverterPowerJsonPattern\":null,\"TelegramBotKey\":\"\",\"TelegramChannelId\":\"\",\"TeslaMateDbServer\":\"database\",\"TeslaMateDbPort\":5432,\"TeslaMateDbDatabaseName\":\"teslamate\",\"TeslaMateDbUser\":\"teslamate\",\"TeslaMateDbPassword\":\"secret\",\"MqqtClientId\":\"TeslaSolarCharger\",\"MosquitoServer\":\"mosquitto\",\"CurrentPowerToGridXmlPattern\":null,\"CurrentPowerToGridXmlAttributeHeaderName\":null,\"CurrentPowerToGridXmlAttributeHeaderValue\":null,\"CurrentPowerToGridXmlAttributeValueName\":null,\"CurrentInverterPowerXmlPattern\":null,\"CurrentInverterPowerXmlAttributeHeaderName\":null,\"CurrentInverterPowerXmlAttributeHeaderValue\":null,\"CurrentInverterPowerXmlAttributeValueName\":null}"; - - var oldBaseConfig = JsonConvert.DeserializeObject(baseConfigJsonString) ?? throw new InvalidOperationException(); - - var newBaseConfig = converter.ConvertV0_1ToV1_0(oldBaseConfig); - - Assert.Equal(-1, newBaseConfig.CurrentPowerToGridCorrectionFactor); - } - - [Fact] - public void Can_Convert_BaseConfigValuesV0_1ToV1_0_ModbusGridUrl() - { - var converter = Mock.Create(); - const string baseConfigJsonString = "{\"LastEditDateTime\":\"2022-07-21T13:44:22.9562369Z\",\"CurrentPowerToGridUrl\":\"http://192.168.1.50:5007/api/ChargingLog/GetCurrentGridPower\",\"CurrentInverterPowerUrl\":\"http://modbusplugin/api/Modbus/GetValue?unitIdentifier=3&startingAddress=30775&quantity=2&ipAddress=192.168.1.28&port=502&factor=1&connectDelaySeconds=0&timeoutSeconds=1&minimumResult=0\",\"TeslaMateApiBaseUrl\":\"http://teslamateapi:8080\",\"UpdateIntervalSeconds\":30,\"PvValueUpdateIntervalSeconds\":1,\"CarPriorities\":\"1|2\",\"GeoFence\":\"Zu Hause\",\"MinutesUntilSwitchOn\":5,\"MinutesUntilSwitchOff\":5,\"PowerBuffer\":0,\"CurrentPowerToGridJsonPattern\":null,\"CurrentPowerToGridInvertValue\":true,\"CurrentInverterPowerJsonPattern\":null,\"TelegramBotKey\":\"\",\"TelegramChannelId\":\"\",\"TeslaMateDbServer\":\"database\",\"TeslaMateDbPort\":5432,\"TeslaMateDbDatabaseName\":\"teslamate\",\"TeslaMateDbUser\":\"teslamate\",\"TeslaMateDbPassword\":\"secret\",\"MqqtClientId\":\"TeslaSolarCharger\",\"MosquitoServer\":\"mosquitto\",\"CurrentPowerToGridXmlPattern\":null,\"CurrentPowerToGridXmlAttributeHeaderName\":null,\"CurrentPowerToGridXmlAttributeHeaderValue\":null,\"CurrentPowerToGridXmlAttributeValueName\":null,\"CurrentInverterPowerXmlPattern\":null,\"CurrentInverterPowerXmlAttributeHeaderName\":null,\"CurrentInverterPowerXmlAttributeHeaderValue\":null,\"CurrentInverterPowerXmlAttributeValueName\":null}"; - - var oldBaseConfig = JsonConvert.DeserializeObject(baseConfigJsonString) ?? throw new InvalidOperationException(); - - var newBaseConfig = converter.ConvertV0_1ToV1_0(oldBaseConfig); - - Assert.Equal(-1, newBaseConfig.CurrentPowerToGridCorrectionFactor); - } - - [Fact] - public void Can_Convert_BaseConfigValuesV0_1ToV1_0_DoesNotUpdateGridUrlWhenNotNeeded() - { - var converter = Mock.Create(); - const string baseConfigJsonString = "{\"LastEditDateTime\":\"2022-07-21T13:44:22.9562369Z\",\"CurrentPowerToGridUrl\":\"http://192.168.1.50:5007/api/ChargingLog/GetCurrentGridPower\",\"CurrentInverterPowerUrl\":\"http://modbusplugin/api/Modbus/GetValue?unitIdentifier=3&startingAddress=30775&quantity=2&ipAddress=192.168.1.28&port=502&factor=1&connectDelaySeconds=0&timeoutSeconds=1&minimumResult=0\",\"TeslaMateApiBaseUrl\":\"http://teslamateapi:8080\",\"UpdateIntervalSeconds\":30,\"PvValueUpdateIntervalSeconds\":1,\"CarPriorities\":\"1|2\",\"GeoFence\":\"Zu Hause\",\"MinutesUntilSwitchOn\":5,\"MinutesUntilSwitchOff\":5,\"PowerBuffer\":0,\"CurrentPowerToGridJsonPattern\":null,\"CurrentPowerToGridInvertValue\":true,\"CurrentInverterPowerJsonPattern\":null,\"TelegramBotKey\":\"\",\"TelegramChannelId\":\"\",\"TeslaMateDbServer\":\"database\",\"TeslaMateDbPort\":5432,\"TeslaMateDbDatabaseName\":\"teslamate\",\"TeslaMateDbUser\":\"teslamate\",\"TeslaMateDbPassword\":\"secret\",\"MqqtClientId\":\"TeslaSolarCharger\",\"MosquitoServer\":\"mosquitto\",\"CurrentPowerToGridXmlPattern\":null,\"CurrentPowerToGridXmlAttributeHeaderName\":null,\"CurrentPowerToGridXmlAttributeHeaderValue\":null,\"CurrentPowerToGridXmlAttributeValueName\":null,\"CurrentInverterPowerXmlPattern\":null,\"CurrentInverterPowerXmlAttributeHeaderName\":null,\"CurrentInverterPowerXmlAttributeHeaderValue\":null,\"CurrentInverterPowerXmlAttributeValueName\":null}"; - - var oldBaseConfig = JsonConvert.DeserializeObject(baseConfigJsonString) ?? throw new InvalidOperationException(); - - var newBaseConfig = converter.ConvertV0_1ToV1_0(oldBaseConfig); - - Assert.Equal(oldBaseConfig.CurrentPowerToGridUrl, newBaseConfig.CurrentPowerToGridUrl); - Assert.Equal(-1, newBaseConfig.CurrentPowerToGridCorrectionFactor); - } - - [Fact] - public void Can_Convert_BaseConfigValuesV0_1ToV1_0_UpdateGridUrlWhenNotNeeded() - { - var converter = Mock.Create(); - const string baseConfigJsonString = "{\"LastEditDateTime\":\"2022-07-21T13:44:22.9562369Z\",\"CurrentPowerToGridUrl\":\"http://modbusplugin/api/Modbus/GetValue?unitIdentifier=1&startingAddress=37113&quantity=2&ipAddress=inverterLocIP&port=502&factor=10&connectDelaySeconds=1&timeoutSeconds=10\",\"CurrentInverterPowerUrl\":\"http://modbusplugin/api/Modbus/GetValue?unitIdentifier=3&startingAddress=30775&quantity=2&ipAddress=192.168.1.28&port=502&factor=1&connectDelaySeconds=0&timeoutSeconds=1&minimumResult=0\",\"TeslaMateApiBaseUrl\":\"http://teslamateapi:8080\",\"UpdateIntervalSeconds\":30,\"PvValueUpdateIntervalSeconds\":1,\"CarPriorities\":\"1|2\",\"GeoFence\":\"Zu Hause\",\"MinutesUntilSwitchOn\":5,\"MinutesUntilSwitchOff\":5,\"PowerBuffer\":0,\"CurrentPowerToGridJsonPattern\":null,\"CurrentPowerToGridInvertValue\":true,\"CurrentInverterPowerJsonPattern\":null,\"TelegramBotKey\":\"\",\"TelegramChannelId\":\"\",\"TeslaMateDbServer\":\"database\",\"TeslaMateDbPort\":5432,\"TeslaMateDbDatabaseName\":\"teslamate\",\"TeslaMateDbUser\":\"teslamate\",\"TeslaMateDbPassword\":\"secret\",\"MqqtClientId\":\"TeslaSolarCharger\",\"MosquitoServer\":\"mosquitto\",\"CurrentPowerToGridXmlPattern\":null,\"CurrentPowerToGridXmlAttributeHeaderName\":null,\"CurrentPowerToGridXmlAttributeHeaderValue\":null,\"CurrentPowerToGridXmlAttributeValueName\":null,\"CurrentInverterPowerXmlPattern\":null,\"CurrentInverterPowerXmlAttributeHeaderName\":null,\"CurrentInverterPowerXmlAttributeHeaderValue\":null,\"CurrentInverterPowerXmlAttributeValueName\":null}"; - - var oldBaseConfig = JsonConvert.DeserializeObject(baseConfigJsonString) ?? throw new InvalidOperationException(); - - var newBaseConfig = converter.ConvertV0_1ToV1_0(oldBaseConfig); - - Assert.Equal("http://modbusplugin/api/Modbus/GetValue?unitIdentifier=1&startingAddress=37113&quantity=2&ipAddress=inverterLocIP&port=502&connectDelaySeconds=1&timeoutSeconds=10", - newBaseConfig.CurrentPowerToGridUrl); - Assert.Equal(-10, newBaseConfig.CurrentPowerToGridCorrectionFactor); - } -} diff --git a/TeslaSolarCharger.Tests/Services/Server/ErrorHandlingService.cs b/TeslaSolarCharger.Tests/Services/Server/ErrorHandlingService.cs new file mode 100644 index 000000000..50b167c90 --- /dev/null +++ b/TeslaSolarCharger.Tests/Services/Server/ErrorHandlingService.cs @@ -0,0 +1,58 @@ +using LanguageExt; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using TeslaSolarCharger.Model.Entities.TeslaSolarCharger; +using TeslaSolarCharger.Shared.Dtos.LoggedError; +using TeslaSolarCharger.Shared.Enums; +using Xunit; +using Xunit.Abstractions; + +namespace TeslaSolarCharger.Tests.Services.Server; + +public class ErrorHandlingService(ITestOutputHelper outputHelper) : TestBase(outputHelper) +{ + [Fact] + public async Task CanGetActiveErrors() + { + var errorHandlingService = Mock.Create(); + var errorsToDisplayFin = await errorHandlingService.GetActiveLoggedErrors(); + errorsToDisplayFin.Match( + Succ: unfilteredErrors => + { + Assert.Equal(2, unfilteredErrors.Count); + Assert.Single(unfilteredErrors, e => e.Id == -1); + Assert.Single(unfilteredErrors, e => e.Id == -2); + }, + Fail: error => + { + throw new Exception(error.Message); + }); + + } + + [Fact] + public async Task CanGetHiddenErrors() + { + var errorHandlingService = Mock.Create(); + var errorsToDisplayFin = await errorHandlingService.GetHiddenErrors(); + errorsToDisplayFin.Match( + Succ: unfilteredErrors => + { + Assert.Equal(2, unfilteredErrors.Count); + Assert.Single(unfilteredErrors, e => e.Id == -3); + Assert.Single(unfilteredErrors, e => e.Id == -4); + var notEnoughOccurrencesElement = unfilteredErrors.Single(e => e.Id == -3); + Assert.Equal(LoggedErrorHideReason.NotEnoughOccurrences, notEnoughOccurrencesElement.HideReason); + + var dismissedElement = unfilteredErrors.Single(e => e.Id == -4); + Assert.Equal(LoggedErrorHideReason.Dismissed, dismissedElement.HideReason); + }, + Fail: error => + { + throw new Exception(error.Message); + }); + + } +} diff --git a/TeslaSolarCharger.Tests/Services/Server/LatestTimeToReachSocUpdateService.cs b/TeslaSolarCharger.Tests/Services/Server/LatestTimeToReachSocUpdateService.cs index d2004ff52..8bdee3719 100644 --- a/TeslaSolarCharger.Tests/Services/Server/LatestTimeToReachSocUpdateService.cs +++ b/TeslaSolarCharger.Tests/Services/Server/LatestTimeToReachSocUpdateService.cs @@ -1,4 +1,4 @@ -using System; +using System; using TeslaSolarCharger.Shared.Contracts; using TeslaSolarCharger.Shared.Dtos.Settings; using TeslaSolarCharger.Shared.TimeProviding; @@ -15,11 +15,12 @@ public LatestTimeToReachSocUpdateService(ITestOutputHelper outputHelper) } [Theory, MemberData(nameof(CorrectData))] - public void Correctly_Updates_LatestTimeToReachSoc(bool shouldIgnoreDate, DateTime currentDate, DateTime configuredDate, DateTime expectedDate) + public void Correctly_Updates_LatestTimeToReachSoc(bool shouldIgnoreDate, bool shouldIgnoreDateOnWeekend, DateTime currentDate, DateTime configuredDate, DateTime expectedDate) { var car = new DtoCar() { IgnoreLatestTimeToReachSocDate = shouldIgnoreDate, + IgnoreLatestTimeToReachSocDateOnWeekend = shouldIgnoreDateOnWeekend, LatestTimeToReachSoC = configuredDate, }; @@ -35,12 +36,14 @@ public void Correctly_Updates_LatestTimeToReachSoc(bool shouldIgnoreDate, DateTi new object[] { true, + false, new DateTime(2023,2,2, 8, 0, 0), new DateTime(2023,1,15, 14, 0, 0), new DateTime(2023, 2, 2, 14, 0, 0), }, new object[] { + false, false, new DateTime(2023,2,2, 8, 0, 0), new DateTime(2023,1,15, 14, 0, 0), @@ -49,12 +52,14 @@ public void Correctly_Updates_LatestTimeToReachSoc(bool shouldIgnoreDate, DateTi new object[] { true, + false, new DateTime(2023,2,2, 8, 0, 0), new DateTime(2023,2,15, 14, 0, 0), new DateTime(2023, 2, 2, 14, 0, 0), }, new object[] { + false, false, new DateTime(2023,2,2, 8, 0, 0), new DateTime(2023,2,15, 14, 0, 0), @@ -62,6 +67,7 @@ public void Correctly_Updates_LatestTimeToReachSoc(bool shouldIgnoreDate, DateTi }, new object[] { + false, false, new DateTime(2023,2,16, 17, 0, 0), new DateTime(2023,2,17, 14, 0, 0), @@ -70,9 +76,50 @@ public void Correctly_Updates_LatestTimeToReachSoc(bool shouldIgnoreDate, DateTi new object[] { true, + false, new DateTime(2023,2,2, 8, 0, 0), new DateTime(2023,2,2, 6, 0, 0), new DateTime(2023, 2, 3, 6, 0, 0), }, + new object[] + { + false, + true, + new DateTime(2023,2,2, 8, 0, 0), + new DateTime(2023,1,15, 14, 0, 0), + new DateTime(2023, 2, 2, 14, 0, 0), + }, + new object[] + { + false, + true, + new DateTime(2023,2,3, 8, 0, 0), + new DateTime(2023,1,15, 10, 0, 0), + new DateTime(2023, 2, 3, 10, 0, 0), + }, + new object[] + { + false, + true, + new DateTime(2023,2,3, 8, 0, 0), + new DateTime(2023,1,15, 6, 0, 0), + new DateTime(2023, 2, 6, 6, 0, 0), + }, + new object[] + { + false, + true, + new DateTime(2023,2,4, 8, 0, 0), + new DateTime(2023,1,15, 10, 0, 0), + new DateTime(2023, 2, 6, 10, 0, 0), + }, + new object[] + { + false, + true, + new DateTime(2023,2,5, 8, 0, 0), + new DateTime(2023,1,15, 6, 0, 0), + new DateTime(2023, 2, 6, 6, 0, 0), + }, }; } diff --git a/TeslaSolarCharger.Tests/Services/Server/TeslaMateApiService.cs b/TeslaSolarCharger.Tests/Services/Server/TeslaMateApiService.cs deleted file mode 100644 index fbe33c7b0..000000000 --- a/TeslaSolarCharger.Tests/Services/Server/TeslaMateApiService.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using TeslaSolarCharger.Shared.Dtos.Settings; -using Xunit; -using Xunit.Abstractions; - -namespace TeslaSolarCharger.Tests.Services.Server; - -public class TeslaMateApiService(ITestOutputHelper outputHelper) : TestBase(outputHelper) -{ - [Theory] - [InlineData(18, null, null, false)] - [InlineData(18, null, 19, true)] - [InlineData(18, null, 17, false)] - [InlineData(18, 17, null, true)] - [InlineData(27, null, 3, false)] - [InlineData(27, 4, 4, false)] - public void CanDecideIfScheduledChargingIsNeeded(int currentDateHour, int? carSetHour, int? carHourToSet, bool expectedResult) - { - var teslamateApiService = Mock.Create(); - - var day = 13; - if (currentDateHour > 24) - { - day++; - currentDateHour -= 24; - } - - var utcOffset = TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow); - var currentDate = new DateTimeOffset(2022, 2, day, currentDateHour, 0, 0, utcOffset); - - DateTimeOffset? setChargeStart = carSetHour == null ? null : - new DateTimeOffset(2022, 2, 14, (int)carSetHour, 0, 0, utcOffset); - var hourDifference = 1; - DateTimeOffset? chargeStartToSet = carHourToSet == null ? null : - //Minutes set to check if is rounding up to next 15 minutes - new DateTimeOffset(2022, 2, 13, (int)carHourToSet - hourDifference, 51, 0, utcOffset); - - var car = new DtoCar() - { - ScheduledChargingStartTime = setChargeStart, - }; - - var isChangeNeeded = teslamateApiService.IsChargingScheduleChangeNeeded(chargeStartToSet, currentDate, car, out var parameters); - - Assert.Equal(expectedResult, isChangeNeeded); - - if (!isChangeNeeded) - { - Assert.Empty(parameters); - } - else - { - Assert.Equal(2, parameters.Count); - if (carHourToSet == null) - { - Assert.Equal("false", parameters["enable"]); - Assert.Equal("0", parameters["time"]); - } - else - { - Assert.Equal("true", parameters["enable"]); - var localhour = chargeStartToSet!.Value.ToLocalTime().TimeOfDay.Hours + hourDifference; - Assert.Equal((localhour * 60).ToString(), parameters["time"]); - } - - } - } - - [Theory] - [InlineData(14, 15, 14, 15)] - [InlineData(14, 16, 14, 30)] - [InlineData(14, 0, 14, 0)] - [InlineData(14, 27, 14, 30)] - public void CanRoundToNextQuarterHour(int hour, int minute, int resultHour, int resultMinute) - { - var inputDateTimeOffset = new DateTimeOffset(2023, 3, 19, hour, minute, 0, TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow)); - var teslamateApiService = Mock.Create(); - var outputTime = teslamateApiService.RoundToNextQuarterHour(inputDateTimeOffset); - - Assert.Equal(resultHour, outputTime.Hour); - Assert.Equal(resultMinute, outputTime.Minute); - } -} diff --git a/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj b/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj index abf13a8e5..c6e3aa4a4 100644 --- a/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj +++ b/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj @@ -9,17 +9,18 @@ - - - - - - + + + + + + + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/TeslaSolarCharger.Tests/TestBase.cs b/TeslaSolarCharger.Tests/TestBase.cs index a6c5862e9..4a5ca3b2e 100644 --- a/TeslaSolarCharger.Tests/TestBase.cs +++ b/TeslaSolarCharger.Tests/TestBase.cs @@ -14,6 +14,9 @@ using System.Linq; using TeslaSolarCharger.Model.Contracts; using TeslaSolarCharger.Model.EntityFramework; +using TeslaSolarCharger.Server.Contracts; +using TeslaSolarCharger.Server.Resources.PossibleIssues; +using TeslaSolarCharger.Server.Resources.PossibleIssues.Contracts; using TeslaSolarCharger.Shared.Contracts; using TeslaSolarCharger.Shared.Resources.Contracts; using TeslaSolarCharger.Shared.TimeProviding; @@ -65,6 +68,8 @@ protected TestBase( _fake = new AutoFake(); _fake.Provide(); + _fake.Provide(); + _fake.Provide(); _fake.Provide(); _fake.Provide(); _fake.Provide(new FakeDateTimeProvider(currentFakeTime)); @@ -75,6 +80,8 @@ protected TestBase( { b.Register((_, _) => Context); b.Register((_, _) => _fake.Resolve()); + b.Register((_, _) => _fake.Resolve()); + b.Register((_, _) => _fake.Resolve()); b.Register((_, _) => _fake.Resolve()); b.Register((_, _) => _fake.Resolve()); b.Register((_, _) => _fake.Resolve()); @@ -118,6 +125,7 @@ protected TestBase( _ctx = _fake.Provide(new TeslaSolarChargerContext(options)); _ctx.Database.EnsureCreated(); _ctx.InitRestValueConfigurations(); + _ctx.InitLoggedErrors(); _ctx.SaveChanges(); DetachAllEntities(); } diff --git a/TeslaSolarCharger/Client/Components/BackendInformationDisplayComponent.razor b/TeslaSolarCharger/Client/Components/BackendInformationDisplayComponent.razor index ff22e0b60..2b821fdfd 100644 --- a/TeslaSolarCharger/Client/Components/BackendInformationDisplayComponent.razor +++ b/TeslaSolarCharger/Client/Components/BackendInformationDisplayComponent.razor @@ -4,11 +4,6 @@ @inject HttpClient HttpClient - -@if (_backendNotifications.Any()) -{ -

Developer's information

-} @foreach(var notification in _backendNotifications) {
@@ -48,9 +43,9 @@ { return notificationType switch { - BackendNotificationType.Warning => Severity.Error, + BackendNotificationType.Warning => Severity.Warning, BackendNotificationType.Error => Severity.Error, - _ => Severity.Info + _ => Severity.Info, }; } diff --git a/TeslaSolarCharger/Client/Components/BackendIssueValidation.razor b/TeslaSolarCharger/Client/Components/BackendIssueValidation.razor deleted file mode 100644 index ee4ec173a..000000000 --- a/TeslaSolarCharger/Client/Components/BackendIssueValidation.razor +++ /dev/null @@ -1,64 +0,0 @@ -@using TeslaSolarCharger.Shared.Dtos -@using TeslaSolarCharger.Shared.Enums -@using System.Timers -@using TeslaSolarCharger.Shared.Contracts -@inject HttpClient HttpClient -@inject IDateTimeProvider DateTimeProvider - -@if (_issues?.Count > 0) -{ -
-

Issues:

- @foreach (var issue in _issues) - { -
"warning", - _ => "info", - })" role="alert"> -
@issue.IssueMessage
- @if (issue.PossibleSolutions.Count > 0) - { -
-
Possible solutions:
-
    - @foreach (var solution in issue.PossibleSolutions) - { -
  • @((MarkupString)solution)
  • - } -
- } -
- } -
-} - - -@code { - private List? _issues; - private Timer? _timer; - - protected override async Task OnInitializedAsync() - { - await UpdateIssues().ConfigureAwait(false); - - _timer = new Timer(); - _timer.Interval = 10000; - _timer.Elapsed += Refresh; - _timer.Start(); - } - - private void Refresh(object? sender, ElapsedEventArgs e) - { - UpdateIssues().GetAwaiter().GetResult(); - } - - private async Task UpdateIssues() - { - var timeZoneOffset = TimeZoneInfo.Local.GetUtcOffset(DateTimeProvider.Now()); - _issues = await HttpClient.GetFromJsonAsync>($"api/Issue/RefreshIssues?utcTimeZoneOffset={timeZoneOffset}"); - this.StateHasChanged(); - } - -} diff --git a/TeslaSolarCharger/Client/Components/BatteryIcon.razor b/TeslaSolarCharger/Client/Components/BatteryIcon.razor new file mode 100644 index 000000000..e3e764cdc --- /dev/null +++ b/TeslaSolarCharger/Client/Components/BatteryIcon.razor @@ -0,0 +1,27 @@ +@if (StateOfCharge != default) +{ + + + + + + @StateOfCharge% + + +} + +@code { + [Parameter] + public int? StateOfCharge { get; set; } + + private string GetBatteryColor(int soc) + { + if (soc > 60) return "#008000aa"; + if (soc > 30) return "yellow"; + return "red"; + } +} \ No newline at end of file diff --git a/TeslaSolarCharger/Client/Components/HiddenErrorsComponent.razor b/TeslaSolarCharger/Client/Components/HiddenErrorsComponent.razor new file mode 100644 index 000000000..37f84e8ab --- /dev/null +++ b/TeslaSolarCharger/Client/Components/HiddenErrorsComponent.razor @@ -0,0 +1,98 @@ +@using TeslaSolarCharger.Client.Helper +@using TeslaSolarCharger.Client.Helper.Contracts +@using TeslaSolarCharger.Shared.Dtos.LoggedError +@using TeslaSolarCharger.Shared.Enums + +@implements IDisposable +@inject ISnackbar Snackar +@inject IHttpClientHelper HttpClientHelper + + +@if (_errors == default) +{ + +} +else if (_errors.Count < 1) +{ + +} +else +{ +
+ + + +
+
+ + Hidden errors + + +
+
+ These errors are currently not resolved but hidden. +
+
+ The list is only updated once per minute +
+
+
+ + @foreach (var error in _errors) + { +
+ +

@(error.Headline + $" occured {error.Occurrences.Count} time(s)")

+
+ Hidden reason: @(error.HideReason == LoggedErrorHideReason.NotEnoughOccurrences ? "Not Enough occurrences" : "Dismissed") +
+
+ @((MarkupString)error.Message) +
+
+
+ + } +
+
+
+
+} + + +@code { + private List? _errors; + + PeriodicTaskHelper? _periodicTaskHelper; + + + protected override void OnInitialized() + { + _periodicTaskHelper = new(); + _periodicTaskHelper.Start(RefreshErrors, TimeSpan.FromMinutes(1)); + } + + private async Task RefreshErrors() + { + _errors = await HttpClientHelper.SendGetRequestWithSnackbarAsync>("api/LoggedErrors/GetHiddenErrors"); + await InvokeAsync(StateHasChanged); + } + + public void Dispose() + { + _periodicTaskHelper?.Dispose(); + } + + private Severity GetSeverity(IssueSeverity issueSeverity) + { + return issueSeverity switch + { + IssueSeverity.Warning => Severity.Warning, + IssueSeverity.Error => Severity.Error, + _ => Severity.Info, + }; + } +} diff --git a/TeslaSolarCharger/Client/Components/LoggedErrorsComponent.razor b/TeslaSolarCharger/Client/Components/LoggedErrorsComponent.razor new file mode 100644 index 000000000..c4f4aa68c --- /dev/null +++ b/TeslaSolarCharger/Client/Components/LoggedErrorsComponent.razor @@ -0,0 +1,104 @@ +@using TeslaSolarCharger.Client.Helper +@using TeslaSolarCharger.Client.Helper.Contracts +@using TeslaSolarCharger.Shared.Dtos +@using TeslaSolarCharger.Shared.Dtos.LoggedError +@using TeslaSolarCharger.Shared.Enums + +@implements IDisposable +@inject ISnackbar Snackar +@inject IHttpClientHelper HttpClient + + +@if (_errors == default) +{ + +} +else +{ + if (_errors.Count > 0) + { +
+ + + +
+
+ + Errors + + +
+
+
+
+ The list is only updated once per minute +
+
+
+ + @foreach (var error in _errors) + { +
+ +

@(error.Headline + $" occured {error.Occurrences.Count} time(s)")

+ @((MarkupString)error.Message) +
+
+ + } +
+
+
+
+ } + +} + + +@code { + private List? _errors; + + PeriodicTaskHelper? _periodicTaskHelper; + + + protected override void OnInitialized() + { + _periodicTaskHelper = new(); + _periodicTaskHelper.Start(RefreshErrors, TimeSpan.FromSeconds(15)); + } + + private async Task RefreshErrors() + { + _errors = await HttpClient.SendGetRequestWithSnackbarAsync>("api/LoggedErrors/GetActiveLoggedErrors"); + await InvokeAsync(StateHasChanged); + } + + public void Dispose() + { + _periodicTaskHelper?.Dispose(); + } + + private async Task DismissError(int errorId) + { + var idToRemove = await HttpClient.SendPostRequestWithSnackbarAsync("api/LoggedErrors/DismissError", new DtoValue(errorId)); + if (_errors == default) + { + return; + } + _errors.RemoveAll(e => e.Id == idToRemove); + } + + private Severity GetSeverity(IssueSeverity issueSeverity) + { + return issueSeverity switch + { + IssueSeverity.Warning => Severity.Warning, + IssueSeverity.Error => Severity.Error, + _ => Severity.Info, + }; + } +} diff --git a/TeslaSolarCharger/Client/Components/MapComponent.razor b/TeslaSolarCharger/Client/Components/MapComponent.razor new file mode 100644 index 000000000..2ba07e181 --- /dev/null +++ b/TeslaSolarCharger/Client/Components/MapComponent.razor @@ -0,0 +1,79 @@ +@page "/map" +@inject IJSRuntime JsRuntime + +
+ +@code { + [Parameter] + public int Radius { get; set; } = 50; // Default radius + [Parameter] + public double? Latitude { get; set; } + [Parameter] + public double? Longitude { get; set; } + + [Parameter] + public EventCallback LatitudeChanged { get; set; } + [Parameter] + public EventCallback LongitudeChanged { get; set; } + + private double? _lastLatitude; + private double? _lastLongitude; + private int? _lastRadius; + + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JsRuntime.InvokeVoidAsync("initializeMap", DotNetObjectReference.Create(this)); + if (Latitude.HasValue && Longitude.HasValue) + { + await JsRuntime.InvokeVoidAsync("updateCircle", Latitude.Value, Longitude.Value, Radius); + _lastLatitude = Latitude; + _lastLongitude = Longitude; + _lastRadius = Radius; + } + } + } + + protected override async Task OnParametersSetAsync() + { + if (Latitude.HasValue && Longitude.HasValue && _lastRadius.HasValue && Radius != _lastRadius) + { + await JsRuntime.InvokeVoidAsync("updateCircle", Latitude.Value, Longitude.Value, Radius); + _lastRadius = Radius; + + } + } + + [JSInvokable] + public async Task UpdateSelection(double lat, double lng) + { + _lastLatitude = lat; + _lastLongitude = lng; + await JsRuntime.InvokeVoidAsync("updateCircle", lat, lng, Radius); + _ = LatitudeChanged.InvokeAsync(lat); + _ = LongitudeChanged.InvokeAsync(lng); + StateHasChanged(); + } + + private async Task RadiusChanged() + { + if (_lastLatitude.HasValue && _lastLongitude.HasValue) + { + await JsRuntime.InvokeVoidAsync("updateCircle", _lastLatitude.Value, _lastLongitude.Value, Radius); + } + } + + public async Task AddCoordinate(double latitude, double longitude) + { + var newCoord = new Coordinate { Lat = latitude, Lng = longitude }; + await JsRuntime.InvokeVoidAsync("addMarker", newCoord.Lat, newCoord.Lng); + } + + public class Coordinate + { + public double Lat { get; set; } + public double Lng { get; set; } + } +} \ No newline at end of file diff --git a/TeslaSolarCharger/Client/Components/ModbusValueConfigurationComponent.razor b/TeslaSolarCharger/Client/Components/ModbusValueConfigurationComponent.razor index 4ef895561..01342e82e 100644 --- a/TeslaSolarCharger/Client/Components/ModbusValueConfigurationComponent.razor +++ b/TeslaSolarCharger/Client/Components/ModbusValueConfigurationComponent.razor @@ -31,7 +31,8 @@ var options = new DialogOptions() { CloseButton = true, - CloseOnEscapeKey = true, + CloseOnEscapeKey = false, + DisableBackdropClick = true, }; var parameters = new DialogParameters { diff --git a/TeslaSolarCharger/Client/Components/MqttValueConfigurationComponent.razor b/TeslaSolarCharger/Client/Components/MqttValueConfigurationComponent.razor index a97afe31f..66beed95a 100644 --- a/TeslaSolarCharger/Client/Components/MqttValueConfigurationComponent.razor +++ b/TeslaSolarCharger/Client/Components/MqttValueConfigurationComponent.razor @@ -31,7 +31,8 @@ var options = new DialogOptions() { CloseButton = true, - CloseOnEscapeKey = true, + CloseOnEscapeKey = false, + DisableBackdropClick = true, }; var parameters = new DialogParameters { diff --git a/TeslaSolarCharger/Client/Components/PlaceholderComponent.razor b/TeslaSolarCharger/Client/Components/PlaceholderComponent.razor new file mode 100644 index 000000000..24066d4c9 --- /dev/null +++ b/TeslaSolarCharger/Client/Components/PlaceholderComponent.razor @@ -0,0 +1,11 @@ +@for (var i = 0; i < Count; i++) +{ + +} + +@code { + + [Parameter] + public int Count { get; set; } = 1; + +} \ No newline at end of file diff --git a/TeslaSolarCharger/Client/Components/PowerFlowComponent.razor b/TeslaSolarCharger/Client/Components/PowerFlowComponent.razor new file mode 100644 index 000000000..d540121fa --- /dev/null +++ b/TeslaSolarCharger/Client/Components/PowerFlowComponent.razor @@ -0,0 +1,632 @@ +@using TeslaSolarCharger.Shared.Dtos +@using TeslaSolarCharger.Shared.Dtos.IndexRazor.PvValues +@using TeslaSolarCharger.Shared.Resources.Contracts +@using TeslaSolarCharger.Client.Helper + +@inject HttpClient HttpClient +@inject IConstants Constants +@implements IDisposable + +@if (_pvValues != default) +{ + + + + + @if (GridLocation != default && InverterLocation != default) + { + + } + + @if (GridLocation != default && HomeLocation != default) + { + + } + + @if (InverterLocation != default && HomeLocation != default) + { + + } + + + @if (InverterLocation != default && BatteryLocation != default) + { + + } + + @if (HomeLocation != default && BatteryLocation != default) + { + + } + + @if (GridLocation != default && BatteryLocation != default) + { + + } + + + @if (HomeLocation != default && EvChargerLocation != default) + { + + } + + @if (HomeLocation == default) + { + + @if (GridLocation != default && EvChargerLocation != default) + { + + } + + @if (InverterLocation != default && EvChargerLocation != default) + { + + } + + @if (EvChargerLocation != default && BatteryLocation != default) + { + + } + } + + + + @foreach (var ball in Balls) + { + + + + + } + + + + @if (GridLocation != default) + { + 0 ? "lightgreen" : "#ff3030"))" + stroke="lightblue" stroke-width="4" /> + } + + @if (InverterLocation != default) + { + + } + + @if (BatteryLocation != default) + { + 0 ? "lightgreen" : "#ff3030"))" + stroke="lightsalmon" stroke-width="4" /> + } + + @if (HomeLocation != default) + { + + } + + @if (EvChargerLocation != default) + { + + } + + + @if (GridLocation != default) + { + +
+ +
@(Math.Abs(_pvValues.GridPower ?? 0)) W
+
+
+ } + @if (InverterLocation != default) + { + +
+ +
@_pvValues.InverterPower W
+
+
+ } + @if (BatteryLocation != default) + { + +
+ +
@Math.Abs(_pvValues.HomeBatteryPower ?? 0) W
+
+
+ } + @if (HomeLocation != default) + { + +
+ +
@CalculateHomePower() W
+
+
+ } + @if (EvChargerLocation != default) + { + +
+ +
@_pvValues.CarCombinedChargingPowerAtHome W
+
+
+ } +
+} + + +@code { + private List Balls { get; set; } = new(); + + private DtoPvValues? _pvValues; + private bool? _isSolarEdgeInstallation; + private bool? _couldNotRefreshStates; + + PeriodicTaskHelper? _periodicTaskHelper; + + + + + + protected override async Task OnInitializedAsync() + { + var dtoSolarChargerInstallation = await HttpClient.GetFromJsonAsync>("api/Hello/IsSolarEdgeInstallation").ConfigureAwait(false); + _isSolarEdgeInstallation = dtoSolarChargerInstallation?.Value; + + _periodicTaskHelper = new(); + _periodicTaskHelper.Start(RefreshPvValues, TimeSpan.FromSeconds(5)); + } + + private async Task RefreshPvValues() + { + try + { + _pvValues = await HttpClient.GetFromJsonAsync("api/Index/GetPvValues").ConfigureAwait(false); + await InvokeAsync(StateHasChanged); + _couldNotRefreshStates = false; + UpdateBalls(); + } + catch (Exception ex) + { + _couldNotRefreshStates = true; + } + } + + private void UpdateBalls() + { + if (_pvValues == default) + { + return; + } + Balls.Clear(); + + if (_pvValues.InverterPower > 0) + { + if (InverterLocation != default) + { + if (GridLocation != default && _pvValues.GridPower > 0) + { + Balls.Add(new Ball + { + StartX = InverterLocation.X, + StartY = InverterLocation.Y, + EndX = GridLocation.X, + EndY = GridLocation.Y, + Color = "orange", + Duration = CalculateDuration(InverterLocation, GridLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + if (HomeLocation != default) + { + Balls.Add(new Ball + { + StartX = InverterLocation.X, + StartY = InverterLocation.Y, + EndX = HomeLocation.X, + EndY = HomeLocation.Y, + Color = "orange", + Duration = CalculateDuration(InverterLocation, HomeLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + if (BatteryLocation != default && _pvValues.HomeBatteryPower > 0) + { + Balls.Add(new Ball + { + StartX = InverterLocation.X, + StartY = InverterLocation.Y, + EndX = BatteryLocation.X, + EndY = BatteryLocation.Y, + Color = "orange", + Duration = CalculateDuration(InverterLocation, BatteryLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + } + } + + // Grid power flow + if (_pvValues.GridPower < 0) + { + if (GridLocation != default) + { + if (HomeLocation != default && CalculateHomePower() + _pvValues.CarCombinedChargingPowerAtHome > 0) + { + Balls.Add(new Ball + { + StartX = GridLocation.X, + StartY = GridLocation.Y, + EndX = HomeLocation.X, + EndY = HomeLocation.Y, + Color = "lightblue", + Duration = CalculateDuration(GridLocation, HomeLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + + if (BatteryLocation != default && _pvValues.HomeBatteryPower > 0) + { + Balls.Add(new Ball + { + StartX = GridLocation.X, + StartY = GridLocation.Y, + EndX = BatteryLocation.X, + EndY = BatteryLocation.Y, + Color = "lightblue", + Duration = CalculateDuration(GridLocation, BatteryLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + } + } + + // Home Battery power flow + if (_pvValues.HomeBatteryPower < 0) // Battery discharging + { + if (BatteryLocation != default) + { + if (HomeLocation != default && CalculateHomePower() + _pvValues.CarCombinedChargingPowerAtHome > 0) + { + Balls.Add(new Ball + { + StartX = BatteryLocation.X, + StartY = BatteryLocation.Y, + EndX = HomeLocation.X, + EndY = HomeLocation.Y, + Color = "lightsalmon", + Duration = CalculateDuration(BatteryLocation, HomeLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + if (GridLocation != default && _pvValues.GridPower > 0) + { + Balls.Add(new Ball + { + StartX = BatteryLocation.X, + StartY = BatteryLocation.Y, + EndX = GridLocation.X, + EndY = GridLocation.Y, + Color = "lightsalmon", + Duration = CalculateDuration(BatteryLocation, GridLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + } + } + + //EvCharger flow + if (_pvValues.CarCombinedChargingPowerAtHome > 0 && EvChargerLocation != default) + { + var homeLocation = HomeLocation; + if (homeLocation != default) + { + Balls.Add(new Ball + { + StartX = homeLocation.X, + StartY = homeLocation.Y, + EndX = EvChargerLocation.X, + EndY = EvChargerLocation.Y, + Color = "pink", + Duration = CalculateDuration(homeLocation, EvChargerLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + else + { + if (InverterLocation != default && _pvValues.InverterPower > 0) + { + Balls.Add(new Ball + { + StartX = InverterLocation.X, + StartY = InverterLocation.Y, + EndX = EvChargerLocation.X, + EndY = EvChargerLocation.Y, + Color = "orange", + Duration = CalculateDuration(InverterLocation, EvChargerLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + + if (GridLocation != default && _pvValues.GridPower < 0) + { + Balls.Add(new Ball + { + StartX = GridLocation.X, + StartY = GridLocation.Y, + EndX = EvChargerLocation.X, + EndY = EvChargerLocation.Y, + Color = "lightblue", + Duration = CalculateDuration(GridLocation, EvChargerLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + + if (BatteryLocation != default && _pvValues.HomeBatteryPower < 0) + { + Balls.Add(new Ball + { + StartX = BatteryLocation.X, + StartY = BatteryLocation.Y, + EndX = EvChargerLocation.X, + EndY = EvChargerLocation.Y, + Color = "lightsalmon", + Duration = CalculateDuration(BatteryLocation, EvChargerLocation).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) + "s", + }); + } + } + } + InvokeAsync(StateHasChanged); + } + + private float CalculateDuration(Location start, Location end) + { + var deltaX = start.X - end.X; + var deltaY = start.Y - end.Y; + var distance = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY); + return distance / 100; + } + + private int SvgHeight + { + get + { + var size = 0; + if (InverterLocation?.Y > size) + { + size = InverterLocation.Y; + } + if (GridLocation?.Y > size) + { + size = GridLocation.Y; + } + if (BatteryLocation?.Y > size) + { + size = BatteryLocation.Y; + } + if (HomeLocation?.Y > size) + { + size = HomeLocation.Y; + } + if (EvChargerLocation?.Y > size) + { + size = EvChargerLocation.Y; + } + + return size > 0 ? size + 60 : 0; + } + } + + private Location? InverterLocation + { + get + { + if (_pvValues?.InverterPower != default) + { + return new Location() + { + X = SecondColumnX, + Y = FirstLineY, + }; + } + return null; + } + } + + private Location? GridLocation + { + get + { + if (_pvValues?.GridPower == default) + { + return null; + } + + var y = SecondLineY; + if (_pvValues?.InverterPower == default) + { + y = FirstLineY; + } + + return new Location() + { + X = FirstColumnX, + Y = y, + }; + } + } + + private Location? BatteryLocation + { + get + { + if (_pvValues?.HomeBatteryPower == default) + { + return null; + } + var y = SecondLineY; + if (_pvValues?.InverterPower == default) + { + y = FirstLineY; + } + return new Location() + { + X = ThirdColumnX, + Y = y, + }; + } + } + + private Location? HomeLocation + { + get + { + var homePower = CalculateHomePower(); + if (homePower == default || _pvValues == default) + { + return null; + } + var location = GetFreePlaceAfterSolarGridAndBattery(); + if (location?.Y == ThirdLineY) + { + location.X = (SecondColumnX - FirstColumnX) / 2 + FirstColumnX; + } + + return location; + } + } + + private Location? EvChargerLocation + { + get + { + if (_pvValues == default) + { + return null; + } + var homeLocation = HomeLocation; + if (homeLocation == default) + { + return GetFreePlaceAfterSolarGridAndBattery(); + } + + if (homeLocation.Y == SecondLineY) + { + if (homeLocation.X == SecondColumnX) + { + return new Location() + { + X = ThirdColumnX, + Y = SecondLineY, + }; + } + else + { + return new Location() + { + X = SecondColumnX, + Y = ThirdLineY, + }; + } + + } + + return new Location() + { + X = (ThirdColumnX - SecondColumnX) / 2 + SecondColumnX, + Y = ThirdLineY, + }; + } + } + + private Location? GetFreePlaceAfterSolarGridAndBattery() + { + var lineNumber = 1; + if (_pvValues == default) + { + return null; + } + if (_pvValues.InverterPower != default) + { + lineNumber++; + } + if (_pvValues.GridPower != default && _pvValues.HomeBatteryPower != default) + { + lineNumber++; + } + + var yCoordinate = lineNumber switch + { + 1 => FirstLineY, + 2 => SecondLineY, + 3 => ThirdLineY, + _ => throw new ArgumentOutOfRangeException(nameof(lineNumber), lineNumber, "Value must be 1, 2 or 3."), + }; + var xCoordinate = SecondColumnX; + if (_pvValues.GridPower == default) + { + xCoordinate = ThirdColumnX; + } + return new Location() + { + X = xCoordinate, + Y = yCoordinate, + }; + } + + private const int FirstLineY = 45; + private const int SecondLineY = 145; + private const int ThirdLineY = 245; + + private const int FirstColumnX = 70; + private const int SecondColumnX = 210; + private const int ThirdColumnX = 350; + + private const int CircleContentLocationDifference = 35; + + public class Location + { + public int X { get; set; } + public int Y { get; set; } + } + + public class Ball + { + public int StartX { get; set; } + public int StartY { get; set; } + public int EndX { get; set; } + public int EndY { get; set; } + public string Color { get; set; } + public string Duration { get; set; } + public int X => StartX; + public int Y => StartY; + } + + public void Dispose() + { + _periodicTaskHelper?.Dispose(); + } + + private int? CalculateHomePower() + { + if (_pvValues == null) + { + return null; + } + + if (_pvValues.InverterPower == null) + { + return null; + } + + if (_pvValues.GridPower == null) + { + return null; + } + var homeBatteryPower = _pvValues.HomeBatteryPower ?? 0; + var chargingPower = _pvValues.CarCombinedChargingPowerAtHome ?? 0; + var homePower = _pvValues.InverterPower - _pvValues.GridPower - homeBatteryPower - chargingPower; + return homePower; + } +} \ No newline at end of file diff --git a/TeslaSolarCharger/Client/Components/RestValueConfigurationComponent.razor b/TeslaSolarCharger/Client/Components/RestValueConfigurationComponent.razor index b497debac..72a8db578 100644 --- a/TeslaSolarCharger/Client/Components/RestValueConfigurationComponent.razor +++ b/TeslaSolarCharger/Client/Components/RestValueConfigurationComponent.razor @@ -1,9 +1,5 @@ -@using TeslaSolarCharger.Shared.Dtos.RestValueConfiguration -@using TeslaSolarCharger.Shared.Helper.Contracts +@using TeslaSolarCharger.Shared.Helper.Contracts @using TeslaSolarCharger.Shared.Resources.Contracts -@using TeslaSolarCharger.SharedModel.Enums -@using TeslaSolarCharger.Client.Wrapper -@using TeslaSolarCharger.Shared.Dtos @using TeslaSolarCharger.Shared.Dtos.BaseConfiguration @using TeslaSolarCharger.Client.Dialogs @inject HttpClient HttpClient @@ -37,7 +33,8 @@ var options = new DialogOptions() { CloseButton = true, - CloseOnEscapeKey = true, + CloseOnEscapeKey = false, + DisableBackdropClick = true, }; var parameters = new DialogParameters { diff --git a/TeslaSolarCharger/Client/Components/RightAlignedButtonComponent.razor b/TeslaSolarCharger/Client/Components/RightAlignedButtonComponent.razor index c9f98f052..35dc60bbb 100644 --- a/TeslaSolarCharger/Client/Components/RightAlignedButtonComponent.razor +++ b/TeslaSolarCharger/Client/Components/RightAlignedButtonComponent.razor @@ -1,18 +1,27 @@  - @if (IsDisabled) + @if (IsDisabled && !string.IsNullOrEmpty(DisableToolTipText)) { - @ButtonText + @ButtonText - Save basic data before adding + @DisableToolTipText } else { - + @if (IsLoading) { @@ -34,10 +43,14 @@ public string ButtonText { get; set; } [Parameter] public string StartIcon { get; set; } - + [Parameter] + public string? DisableToolTipText { get; set; } [Parameter] public bool IsLoading { get; set; } + [Parameter] + public ButtonType ButtonType { get; set; } = ButtonType.Button; + [Parameter] public EventCallback OnButtonClicked { get; set; } diff --git a/TeslaSolarCharger/Client/Dialogs/AddCarDialog.razor b/TeslaSolarCharger/Client/Dialogs/AddCarDialog.razor new file mode 100644 index 000000000..40887c6f0 --- /dev/null +++ b/TeslaSolarCharger/Client/Dialogs/AddCarDialog.razor @@ -0,0 +1,44 @@ +@using TeslaSolarCharger.Shared.Dtos +@using TeslaSolarCharger.Shared.Enums + +@inject HttpClient HttpClient + +

AddCarDialog

+ +@if (_fleetApiTokenState == default) +{ + +} +else if (_fleetApiTokenState != FleetApiTokenState.UpToDate) +{ + +

Tesla Fleet API Token is not valid.

+ Go to Base Configuration and Generate a Tesla Fleet API Token. +
+} +else +{ + +} + +@code { + + + private FleetApiTokenState? _fleetApiTokenState; + + protected override async Task OnInitializedAsync() + { + await RefreshFleetApiTokenState(); + } + + public async Task RefreshFleetApiTokenState() + { + var value = await HttpClient.GetFromJsonAsync>("api/FleetApi/FleetApiTokenState").ConfigureAwait(false); + if (value != null) + { + _fleetApiTokenState = value.Value; + } + } +} diff --git a/TeslaSolarCharger/Client/Dialogs/TextDialog.razor b/TeslaSolarCharger/Client/Dialogs/TextDialog.razor new file mode 100644 index 000000000..9ab0438eb --- /dev/null +++ b/TeslaSolarCharger/Client/Dialogs/TextDialog.razor @@ -0,0 +1,18 @@ + + + @(Text) + + + OK + + + +@code { + [CascadingParameter] MudDialogInstance MudDialog { get; set; } + + [Parameter] + public string Text { get; set; } = ""; + + void Submit() => MudDialog.Close(DialogResult.Ok(true)); + void Cancel() => MudDialog.Cancel(); +} \ No newline at end of file diff --git a/TeslaSolarCharger/Client/Helper/Contracts/IDialogHelper.cs b/TeslaSolarCharger/Client/Helper/Contracts/IDialogHelper.cs new file mode 100644 index 000000000..62ab7f021 --- /dev/null +++ b/TeslaSolarCharger/Client/Helper/Contracts/IDialogHelper.cs @@ -0,0 +1,6 @@ +namespace TeslaSolarCharger.Client.Helper.Contracts; + +public interface IDialogHelper +{ + Task ShowTextDialog(string title, string dialogText); +} diff --git a/TeslaSolarCharger/Client/Helper/Contracts/IHttpClientHelper.cs b/TeslaSolarCharger/Client/Helper/Contracts/IHttpClientHelper.cs new file mode 100644 index 000000000..f7a6fb0c2 --- /dev/null +++ b/TeslaSolarCharger/Client/Helper/Contracts/IHttpClientHelper.cs @@ -0,0 +1,9 @@ +namespace TeslaSolarCharger.Client.Helper.Contracts; + +public interface IHttpClientHelper +{ + Task SendGetRequestWithSnackbarAsync(string url); + Task SendGetRequestWithSnackbarAsync(string url); + Task SendPostRequestWithSnackbarAsync(string url, object content); + Task SendPostRequestWithSnackbarAsync(string url, object content); +} diff --git a/TeslaSolarCharger/Client/Helper/DialogHelper.cs b/TeslaSolarCharger/Client/Helper/DialogHelper.cs new file mode 100644 index 000000000..46f08995c --- /dev/null +++ b/TeslaSolarCharger/Client/Helper/DialogHelper.cs @@ -0,0 +1,23 @@ +using MudBlazor; +using TeslaSolarCharger.Client.Dialogs; +using TeslaSolarCharger.Client.Helper.Contracts; + +namespace TeslaSolarCharger.Client.Helper; + +public class DialogHelper(IDialogService dialogService) : IDialogHelper +{ + public async Task ShowTextDialog(string title, string dialogText) + { + var options = new DialogOptions() + { + CloseButton = true, + CloseOnEscapeKey = true, + }; + var parameters = new DialogParameters + { + { x => x.Text, dialogText }, + }; + var dialog = await dialogService.ShowAsync(title, parameters, options); + var result = await dialog.Result; + } +} diff --git a/TeslaSolarCharger/Client/Helper/HttpClientHelper.cs b/TeslaSolarCharger/Client/Helper/HttpClientHelper.cs new file mode 100644 index 000000000..ad3faa572 --- /dev/null +++ b/TeslaSolarCharger/Client/Helper/HttpClientHelper.cs @@ -0,0 +1,106 @@ +using Microsoft.AspNetCore.Mvc; +using MudBlazor; +using Newtonsoft.Json; +using System.Net.Http.Json; +using TeslaSolarCharger.Client.Helper.Contracts; + +namespace TeslaSolarCharger.Client.Helper; + +public class HttpClientHelper(HttpClient httpClient, ISnackbar snackbar, IDialogHelper dialogHelper) : IHttpClientHelper +{ + public async Task SendGetRequestWithSnackbarAsync(string url) + { + return await SendRequestWithSnackbarInternalAsync(HttpMethod.Get, url, null); + } + + public async Task SendGetRequestWithSnackbarAsync(string url) + { + await SendRequestWithSnackbarInternalAsync(HttpMethod.Get, url, null); + } + + public async Task SendPostRequestWithSnackbarAsync(string url, object content) + { + return await SendRequestWithSnackbarInternalAsync(HttpMethod.Post, url, content); + } + + public async Task SendPostRequestWithSnackbarAsync(string url, object content) + { + await SendRequestWithSnackbarInternalAsync(HttpMethod.Post, url, content); + } + + private async Task SendRequestWithSnackbarInternalAsync( + HttpMethod method, + string url, + object? content) + { + try + { + HttpResponseMessage response; + if (method == HttpMethod.Get) + { + response = await httpClient.GetAsync(url); + } + else if (method == HttpMethod.Post) + { + var jsonContent = new StringContent( + JsonConvert.SerializeObject(content), + System.Text.Encoding.UTF8, + "application/json"); + response = await httpClient.PostAsync(url, jsonContent); + } + else + { + throw new ArgumentException("Unsupported HTTP method", nameof(method)); + } + + if (response.IsSuccessStatusCode) + { + var responseContent = await response.Content.ReadAsStringAsync(); + if (typeof(T) != typeof(object)) + { + var deserializedObject = JsonConvert.DeserializeObject(responseContent); + if (deserializedObject == null) + { + snackbar.Add($"{url}: The string could not be deserialized to the object type.", Severity.Error); + } + return deserializedObject; + } + + if (string.IsNullOrEmpty(responseContent)) + { + return default; + } + snackbar.Add($"{url}: The specified object type is not supported", Severity.Error); + return default; + } + else if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) + { + var problemDetails = await response.Content.ReadFromJsonAsync(); + var message = problemDetails != null ? $"Error: {problemDetails.Detail}" : "An error occurred"; + snackbar.Add(message, Severity.Error); + } + else + { + var message = $"{url}: Unexpected error: {response.StatusCode}"; + snackbar.Add(message, Severity.Error); + } + } + catch (HttpRequestException ex) + { + var message = $"{url}: Network error: {ex.Message}"; + snackbar.Add(message, Severity.Error); + } + catch (Exception ex) + { + var message = $"{url}: Unexpected error: {ex.Message}"; + snackbar.Add(message, Severity.Error, config => + { + config.Action = "Details"; + config.ActionColor = Color.Primary; + config.Onclick = snackbar1 => dialogHelper.ShowTextDialog("Error Details", + $"Unexpected error while calling {url}: {ex.Message}{Environment.NewLine}{ex.StackTrace}"); + }); + } + return default; + } +} diff --git a/TeslaSolarCharger/Client/Helper/PeriodicTaskHelper.cs b/TeslaSolarCharger/Client/Helper/PeriodicTaskHelper.cs new file mode 100644 index 000000000..4773c46a6 --- /dev/null +++ b/TeslaSolarCharger/Client/Helper/PeriodicTaskHelper.cs @@ -0,0 +1,40 @@ +namespace TeslaSolarCharger.Client.Helper; + +public class PeriodicTaskHelper : IDisposable +{ + private CancellationTokenSource? _cts; + + public void Start(Func action, TimeSpan interval) + { + _cts?.Cancel(); + _cts = new CancellationTokenSource(); + + _ = RunPeriodicAsync(action, interval, _cts.Token); + } + + private async Task RunPeriodicAsync(Func action, TimeSpan interval, CancellationToken token) + { + try + { + while (!token.IsCancellationRequested) + { + await action(); + await Task.Delay(interval, token); + } + } + catch (TaskCanceledException) + { + } + } + + private void Stop() + { + _cts?.Cancel(); + } + + public void Dispose() + { + Stop(); + _cts?.Dispose(); + } +} diff --git a/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor b/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor index bd8e612d5..92594a72c 100644 --- a/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor +++ b/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor @@ -1,5 +1,6 @@ @page "/BaseConfiguration" @using System.Globalization +@using TeslaSolarCharger.Client.Helper.Contracts @using TeslaSolarCharger.Shared.Dtos.BaseConfiguration @using TeslaSolarCharger.Shared @using TeslaSolarCharger.Shared.Enums @@ -7,6 +8,7 @@ @inject HttpClient HttpClient @inject NavigationManager NavigationManager @inject ISnackbar Snackbar +@inject IHttpClientHelper HttpClientHelper Base Configuration @@ -77,37 +79,74 @@ else
}

TeslaMate:

+ + @if (_dtoBaseConfiguration.UseTeslaMateIntegration) + { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + + +

Home Geofence

- - -
- If enabled TeslaMate MQTT is used as datasource. If disabled Tesla API is directly called. -
+ + + Click on the map to select your home geofence. Within that area TSC will regulate the charging power.
- - - - - - - - - - - + + - + @@ -123,7 +162,7 @@ else + HelpText="Set the SoC your home battery should get charged to before cars start to use full power. Leave empty if you do not have a home battery"> @@ -132,7 +171,7 @@ else + HelpText="Set the power your home battery should charge with as long as SoC is below set minimum SoC. Leave empty if you do not have a home battery"> @@ -151,329 +190,24 @@ else +
+

Telegram:

+ How to set up Telegram +
Note: The Telegram bot for now only sends messages if something is not working. E.g. The car does not respond to commands, solar power values can not be refreshed,...
+ + + + +
+ - - @if (_dtoBaseConfiguration.FrontendConfiguration!.GridValueSource == SolarValueSource.Mqtt - || _dtoBaseConfiguration.FrontendConfiguration!.HomeBatteryValuesSource == SolarValueSource.Mqtt - || _dtoBaseConfiguration.FrontendConfiguration!.InverterValueSource == SolarValueSource.Mqtt - ) - { -
-

MQTT Server settings

- - - - - - - - - - - - - - - -
- } - -
-

Grid Power:

- - - - - @foreach (var value in Enum.GetValues()) - { - - } - - - - - @if (_dtoBaseConfiguration.FrontendConfiguration!.GridValueSource == SolarValueSource.Mqtt) - { - - - - - - } - - @if (_dtoBaseConfiguration.FrontendConfiguration!.GridValueSource is SolarValueSource.Rest or SolarValueSource.Modbus) - { - - } - - @if (_dtoBaseConfiguration.FrontendConfiguration.GridValueSource != SolarValueSource.None) - { - - - - - - - - } -
-
-

Home Battery:

- - - - - @foreach (var value in Enum.GetValues()) - { - - } - - - - - @if (_dtoBaseConfiguration.FrontendConfiguration.HomeBatteryValuesSource != SolarValueSource.None) - { -
Home Battery Soc:
- } - @if (_dtoBaseConfiguration.FrontendConfiguration.HomeBatteryValuesSource == SolarValueSource.Mqtt) - { - - - - - - } - - - - @if (_dtoBaseConfiguration.FrontendConfiguration.HomeBatteryValuesSource is SolarValueSource.Rest or SolarValueSource.Modbus) - { - - } - @if (_dtoBaseConfiguration.FrontendConfiguration.HomeBatteryValuesSource != SolarValueSource.None) - { - - - - - - - - - - } - - - @if (_dtoBaseConfiguration.FrontendConfiguration.HomeBatteryValuesSource != SolarValueSource.None) - { -
Home Battery Power:
- } - @if (_dtoBaseConfiguration.FrontendConfiguration.HomeBatteryValuesSource == SolarValueSource.Mqtt) - { - - - - - - } - - - @if (_dtoBaseConfiguration.FrontendConfiguration.HomeBatteryValuesSource is SolarValueSource.Rest or SolarValueSource.Modbus) - { - - } - @if (_dtoBaseConfiguration.FrontendConfiguration.HomeBatteryValuesSource != SolarValueSource.None) - { - - - - - - - - - - - } - -
-
-

Inverter:

- - - - @foreach (var value in Enum.GetValues()) - { - - } - - - - - @if (_dtoBaseConfiguration.FrontendConfiguration.InverterValueSource == SolarValueSource.Mqtt) - { - - - - - - } - - @if (_dtoBaseConfiguration.FrontendConfiguration.InverterValueSource is SolarValueSource.Rest or SolarValueSource.Modbus) - { - - } - @if (_dtoBaseConfiguration.FrontendConfiguration.InverterValueSource != SolarValueSource.None) - { - - - - - - - - } -
-
-

Telegram:

- How to set up Telegram - - - - - - - - - - - -
-

- - @if (_fleetApiTokenState == FleetApiTokenState.NotNeeded) - { - - - - - -
- } - - - - - - - - - - - - -
- - - - - - - - +
+ +
+ } @@ -586,6 +283,9 @@ else private bool _tokenGenerationButtonDisabled; + private bool _telegramSettingsChanged; + private bool _submitLoading; + protected override async Task OnInitializedAsync() { _dtoBaseConfiguration = await HttpClient.GetFromJsonAsync("/api/BaseConfiguration/GetBaseConfiguration").ConfigureAwait(false); @@ -598,15 +298,19 @@ else private async Task HandleValidSubmit() { + _submitLoading = true; var result = await HttpClient.PutAsJsonAsync("api/BaseConfiguration/UpdateBaseConfiguration", _dtoBaseConfiguration).ConfigureAwait(false); if (result.IsSuccessStatusCode) { + _telegramSettingsChanged = false; Snackbar.Add("Base Configuration updated", Severity.Success); } else { Snackbar.Add("Error updating base configuration", Severity.Error); } + + _submitLoading = false; } private async Task GenerateFleetApiToken() @@ -623,4 +327,17 @@ else private string _correctionFactorHelpText = "Use this to correct the returned value. E.g. if the returned value is 1 but should bei -1 insert -1"; + + private async Task SendTelegramTestMessage() + { + var result = await HttpClientHelper.SendGetRequestWithSnackbarAsync>("api/Hello/SendTestTelegramMessage"); + if (result == default) + { + Snackbar.Add("Could not get result", Severity.Error); + return; + } + + Snackbar.Add(result.Value, Severity.Success); + } + } diff --git a/TeslaSolarCharger/Client/Pages/CarSettings.razor b/TeslaSolarCharger/Client/Pages/CarSettings.razor index 5eeb1137d..f80689bc9 100644 --- a/TeslaSolarCharger/Client/Pages/CarSettings.razor +++ b/TeslaSolarCharger/Client/Pages/CarSettings.razor @@ -1,14 +1,46 @@ -@page "/CarSettings" +@page "/CarSettings" @using TeslaSolarCharger.Shared.Dtos @using TeslaSolarCharger.Client.Wrapper @using TeslaSolarCharger.Shared.Dtos.Ble @using Newtonsoft.Json +@using TeslaSolarCharger.Shared.Enums @inject HttpClient HttpClient @inject ISnackbar Snackbar Car Settings

Car Settings

+
+ @if (_fleetApiTokenState == FleetApiTokenState.NotReceived) + { + +

Waiting for token

+ You already requested a token. TSC is currently waiting for receiving it. This might take up to five minutes. Reload this page to get new information if available. +
+ } + else if (_fleetApiTokenState != null && _fleetApiTokenState != FleetApiTokenState.UpToDate) + { + +

Create Token.

+ Go to Base Configuration, Generate a Tesla Fleet API Token and restart TSC to see cars here. +
+ } + @if (_fleetApiTokenState == FleetApiTokenState.UpToDate) + { + +
Restart TSC to add new cars
+ If you do not see all cars here that are available in your Tesla account, restart TSC. +
+ } +
+ + @if (_carBasicConfigurations == null) {
@@ -26,10 +58,11 @@ else - - +
@@ -51,7 +84,7 @@ else
Note: When clicking the pair button the car won't display any feedback. You have to place the card on the center console. Only after doing so, a message will pop up. If you don't see a message, the pairing failed. As the car does not send any feedback, just try a few times, if it still does not work reboot your BLE device.
- +
Test BLE access
Before you can test BLE access you must pair the car with TSC. This includes placing the card on your center console and confirming the new "phone key" on the car's screen. @@ -63,12 +96,30 @@ else { } - + @if (carBasicConfiguration.UseBleForWakeUp) + { +
Test Wakeup via BLE
+
+ After this test the car should wake up. +
+ @if (_bleWakeUpTestResults.TryGetValue(carBasicConfiguration.Vin, out var bleWakeUpResult)) + { + + + } + + } + +
} } @@ -76,18 +127,30 @@ else @code { private List? _carBasicConfigurations; private readonly List _savingCarIds = new(); + private FleetApiTokenState? _fleetApiTokenState; private Dictionary _pairingResults = new(); - private Dictionary _bleTestResults = new(); + private Dictionary _bleTestResults = new(); + private Dictionary _bleWakeUpTestResults = new(); private HashSet _loadingVins = new(); protected override async Task OnInitializedAsync() { + await RefreshFleetApiTokenState(); _carBasicConfigurations = await HttpClient.GetFromJsonAsync>("/api/Config/GetCarBasicConfigurations").ConfigureAwait(false); } + private async Task RefreshFleetApiTokenState() + { + var value = await HttpClient.GetFromJsonAsync>("api/FleetApi/FleetApiTokenState").ConfigureAwait(false); + if (value != null) + { + _fleetApiTokenState = value.Value; + } + } + private async Task UpdateCarConfiguration(int carId, CarBasicConfiguration carBasicConfiguration) { _savingCarIds.Add(carId); @@ -103,14 +166,15 @@ else _savingCarIds.RemoveAll(i => i == carId); } - private async Task PairCar(string vin) + private async Task PairCar(CarBasicConfiguration car) { - _pairingResults.Remove(vin); - _loadingVins.Add(vin); - var result = await HttpClient.GetStringAsync($"/api/Ble/PairKey?vin={vin}").ConfigureAwait(false); - var resultJson = JsonConvert.DeserializeObject(result); - _pairingResults[vin] = resultJson?.Message ?? result; - _loadingVins.Remove(vin); + _pairingResults.Remove(car.Vin); + _loadingVins.Add(car.Vin); + var url = $"/api/Ble/PairKey?vin={car.Vin}&apiRole=charging_manager"; + var result = await HttpClient.GetStringAsync(url).ConfigureAwait(false); + var resultJson = JsonConvert.DeserializeObject(result); + _pairingResults[car.Vin] = resultJson?.ResultMessage ?? result; + _loadingVins.Remove(car.Vin); } private async Task TestBle(string vin) @@ -118,13 +182,37 @@ else _bleTestResults.Remove(vin); _loadingVins.Add(vin); var resultString = await HttpClient.GetStringAsync($"/api/Ble/SetAmp?vin={vin}&s=7").ConfigureAwait(false); - var result = JsonConvert.DeserializeObject(resultString) ?? new DtoBleResult { Success = false, Message = "Could not deserialize message from TSC." }; - if(result.Success && string.IsNullOrWhiteSpace(result.Message)) + var result = JsonConvert.DeserializeObject(resultString) + ?? new DtoBleCommandResult + { + Success = false, + ResultMessage = "Could not deserialize message from TSC.", + }; + if(result.Success && string.IsNullOrWhiteSpace(result.ResultMessage)) { - result.Message = "Ble access seems to work. Please double check if the charge current was set to 7A. Note: As TSC starts using BLE as soon as it is working you might see the 7A only for a short time as TSC changes it every 30 seconds by default."; + result.ResultMessage = "Ble access seems to work. Please double check if the charge current was set to 7A. Note: As TSC starts using BLE as soon as it is working you might see the 7A only for a short time as TSC changes it every 30 seconds by default."; } _bleTestResults[vin] = result; _loadingVins.Remove(vin); } + private async Task WakeCar(string vin) + { + _bleWakeUpTestResults.Remove(vin); + _loadingVins.Add(vin); + var resultString = await HttpClient.GetStringAsync($"/api/Ble/WakeUp?vin={vin}").ConfigureAwait(false); + var result = JsonConvert.DeserializeObject(resultString) + ?? new DtoBleCommandResult + { + Success = false, + ResultMessage = "Could not deserialize message from TSC.", + }; + if (result.Success && string.IsNullOrWhiteSpace(result.ResultMessage)) + { + result.ResultMessage = "The car accepted the wake call."; + } + _bleWakeUpTestResults[vin] = result; + _loadingVins.Remove(vin); + } + } \ No newline at end of file diff --git a/TeslaSolarCharger/Client/Pages/Index.razor b/TeslaSolarCharger/Client/Pages/Index.razor index 94ae72270..6deca8051 100644 --- a/TeslaSolarCharger/Client/Pages/Index.razor +++ b/TeslaSolarCharger/Client/Pages/Index.razor @@ -1,16 +1,18 @@ @page "/" @using System.Globalization -@using System.Timers @using TeslaSolarCharger.Shared @using TeslaSolarCharger.Shared.Dtos @using TeslaSolarCharger.Shared.Dtos.IndexRazor.CarValues -@using TeslaSolarCharger.Shared.Dtos.IndexRazor.PvValues @using TeslaSolarCharger.Shared.Enums @using TeslaSolarCharger.Shared.Resources @using TeslaSolarCharger.Shared.Dtos.Settings @using TeslaSolarCharger.Shared.Dtos.Table -@using System.Diagnostics +@using TeslaSolarCharger.Client.Helper @using TeslaSolarCharger.Shared.Resources.Contracts + +@implements IDisposable + + @inject HttpClient HttpClient @inject ISnackbar Snackbar @inject ToolTipTextKeys ToolTipTextKeys @@ -37,85 +39,12 @@ } - + -@if (_pvValues != null) -{ -
- @if (_couldNotRefreshStates == true) - { - States could not be refreshed. Are you currently offline? - } - @if (_isSolarEdgeInstallation == true) - { - As you are using SolarEdge which is rate limited, these values might be delayed or grid power and batterypower might be displayed as zero or your configured Home Battery charging power (W). This is nothing to worry about, it is just a way to handle the rate limitation. - } - @if (_pvValues.InverterPower != null) - { -
- - - @_pvValues.InverterPower W - -
- } - @if (_pvValues.GridPower != null) - { -
-
- - - @Math.Abs((int)_pvValues.GridPower) W - -
-
- } +
+ +
- @if (_pvValues.HomeBatterySoc != null) - { -
- - - @_pvValues.HomeBatterySoc % - -
- } - @if (_pvValues.HomeBatteryPower != null) - { -
-
- - - @if (_pvValues.HomeBatteryPower != null) - { - @Math.Abs((int)_pvValues.HomeBatteryPower) - } - W - -
-
- } - @if (_pvValues.PowerBuffer != null) - { -
- - instant_mix - - - @_pvValues.PowerBuffer W - -
- } -
- - ev_station - - - @_pvValues.CarCombinedChargingPowerAtHome W - -
-
-} @if (_carBaseStates == null || _carBaseSettings == null) { @@ -125,9 +54,12 @@ else { @if (_carBaseStates.Count < 1) { -

- You need to enable TeslaSolarCharger for at least one car in the car settings page. -

+ +

Enable TeslaSolarCharger for cars

+ You need to enable TeslaSolarCharger for at least one car in the car settings page. +
} @foreach (var car in _carBaseStates) @@ -140,68 +72,63 @@ else @car.HomeChargePower W - + @if (DateTime.UtcNow < car.RateLimitedUntil) { } - - @if (_usingFleetApi == true && car.VehicleCommandProtocolRequired) + @if (_testingFleetApiCarIds.Any(i => i == car.CarId)) { - @if (_testingFleetApiCarIds.Any(i => i == car.CarId)) +
+ + Testing Fleet API access might take about 30 seconds and the headlights might flash... +
+ } + else if (_isFleetApiWorkingForCar.Any(d => d.Key == car.CarId)) + { + @if (_isFleetApiWorkingForCar[car.CarId] == true) { -
- - Testing Fleet API access might take about 30 seconds and the chargeport might open... + } + else + { + } - +
@@ -294,19 +221,32 @@ else UnitText="%" HelpText=""> - + } @if (_carBaseSettings[car.CarId].ChargeMode is ChargeMode.PvOnly or ChargeMode.SpotPrice) {
- +
- @if (!_carBaseSettings[car.CarId].IgnoreLatestTimeToReachSocDate) +
+ + +
+ + @if (!_carBaseSettings[car.CarId].IgnoreLatestTimeToReachSocDate && !_carBaseSettings[car.CarId].IgnoreLatestTimeToReachSocDateOnWeekend) { 0) {

- Car will @((car.State == CarStateEnum.Charging && car.IsHome) ? "stop" : "start") charging when following conditions are met:
+ Car will @((car.State == CarStateEnum.Charging && car.IsHome) ? "stop" : "start") charging when following conditions are met:

    @foreach (var chargeInfo in car.ChargeInformation) { @@ -386,7 +326,9 @@ else
} -
+ + +
@if (_serverTimeZoneDisplayName != default) {
@@ -417,12 +359,11 @@ else }
Installation ID: - +
Language settings: @CultureInfo.CurrentCulture @@ -435,16 +376,16 @@ else
- - - + + +
} @code { - private DtoPvValues? _pvValues; + private bool? _isSolarEdgeInstallation; private bool? _couldNotRefreshStates; private List? _carBaseStates; @@ -457,24 +398,19 @@ else private DateTime? _serverTime; private string? _serverTimeZoneDisplayName; private string _installationId = ""; - private bool? _usingFleetApi; private Dictionary _isFleetApiWorkingForCar = new(); private readonly HashSet _testingFleetApiCarIds = new(); - private Timer? _timer; + PeriodicTaskHelper? _periodicTaskHelper; + protected override async Task OnInitializedAsync() { _toolTipTexts = await HttpClient.GetFromJsonAsync>("api/Index/GetToolTipTexts").ConfigureAwait(false); await RefreshCarBaseSettings().ConfigureAwait(false); await RefreshCarBaseStates().ConfigureAwait(false); - await RefreshPvValues().ConfigureAwait(false); await RefreshServerTime().ConfigureAwait(false); await RefreshServerTimeZone().ConfigureAwait(false); - var dtoSolarChargerInstallation = await HttpClient.GetFromJsonAsync>("api/Hello/IsSolarEdgeInstallation").ConfigureAwait(false); - _isSolarEdgeInstallation = dtoSolarChargerInstallation?.Value; - var usingFleetApi = await HttpClient.GetFromJsonAsync>("api/FleetApi/IsFleetApiEnabled").ConfigureAwait(false); - _usingFleetApi = usingFleetApi?.Value; _version = await HttpClient.GetStringAsync("api/Hello/ProductVersion").ConfigureAwait(false); _installationId = await HttpClient.GetStringAsync("api/Hello/GetInstallationId").ConfigureAwait(false); foreach (var carBaseState in _carBaseStates!) @@ -482,10 +418,8 @@ else _collapsedCarDetails.Add(carBaseState.CarId); } - _timer = new Timer(); - _timer.Interval = Debugger.IsAttached ? 60000 : 5000; - _timer.Elapsed += async (_, _) => await RefreshStates().ConfigureAwait(false); - _timer.Start(); + _periodicTaskHelper = new(); + _periodicTaskHelper.Start(RefreshStates, TimeSpan.FromSeconds(5)); } private async Task RefreshCarBaseStates() @@ -500,10 +434,7 @@ else .ConfigureAwait(false); } - private async Task RefreshPvValues() - { - _pvValues = await HttpClient.GetFromJsonAsync("api/Index/GetPvValues").ConfigureAwait(false); - } + private async Task UpdateCarSettings(DtoCarBaseSettings dtoCarBaseSettings) { @@ -552,39 +483,39 @@ else foreach (var dateValue in dtoCarTopicValues.DateValues) { dataRows.Add(new DtoTableRow() - { - Elements = new List() + { + Elements = new List() { dateValue.Topic, dateValue.DateTime?.ToString("g"), }, - }); + }); } foreach (var nonDateValue in dtoCarTopicValues.NonDateValues) { dataRows.Add(new DtoTableRow() - { - Elements = new List() + { + Elements = new List() { nonDateValue.Topic, nonDateValue.Value, }, - }); + }); } var tableContent = new DtoTableContent() - { - TableHeader = new DtoTableRow() { - Elements = new List() + TableHeader = new DtoTableRow() + { + Elements = new List() { "Topic", "Value", }, - }, - TableData = dataRows, - }; + }, + TableData = dataRows, + }; _newCarDetailStates[carId] = tableContent; } } @@ -619,30 +550,30 @@ else private DtoTableContent GeneratePlannedChargingSlotsTableContent(List chargingSlots) { var table = new DtoTableContent() - { - TableHeader = new DtoTableRow() { - Elements = new List() + TableHeader = new DtoTableRow() + { + Elements = new List() { "Start", "End", "Duration", }, - }, - TableData = new List(), - }; + }, + TableData = new List(), + }; foreach (var chargingSlot in chargingSlots) { table.TableData.Add(new DtoTableRow() - { - Elements = new List() + { + Elements = new List() { chargingSlot.ChargeStart.ToLocalTime().DateTime.ToString("g"), chargingSlot.ChargeEnd.ToLocalTime().DateTime.ToString("g"), chargingSlot.ChargeDuration < TimeSpan.FromDays(1) ? chargingSlot.ChargeDuration.ToString(@"hh\:mm") : string.Empty, }, - IsActive = chargingSlot.IsActive, - }); + IsActive = chargingSlot.IsActive, + }); } return table; @@ -652,12 +583,10 @@ else { try { - await RefreshPvValues().ConfigureAwait(false); await RefreshCarBaseStates().ConfigureAwait(false); await RefreshAllVisableCarDetails().ConfigureAwait(false); await RefreshServerTime().ConfigureAwait(false); - - _couldNotRefreshStates = false; + await InvokeAsync(StateHasChanged); } catch (Exception) { @@ -677,6 +606,12 @@ else { _testingFleetApiCarIds.Add(carId); var response = await HttpClient.GetFromJsonAsync>($"api/FleetApi/TestFleetApiAccess?carId={carId}").ConfigureAwait(false); + //Try again if non success because first try may result in no success bacause of Fleet API proxy required is only set after first error. So maybe the error is due to fleet api proxy beeing required but not used. + if (response!.Value == false) + { + await Task.Delay(TimeSpan.FromSeconds(1)); + response = await HttpClient.GetFromJsonAsync>($"api/FleetApi/TestFleetApiAccess?carId={carId}").ConfigureAwait(false); + } if (response != default) { _isFleetApiWorkingForCar[carId] = response.Value; @@ -722,4 +657,31 @@ else } return Icons.Material.Filled.BatteryFull; } -} \ No newline at end of file + + private void UpdateCheckBoxState(int carId, string checkboxName, bool isChecked) + { + if (checkboxName == "IgnoreLatestTimeToReachSocDate") + { + _carBaseSettings[carId].IgnoreLatestTimeToReachSocDate = isChecked; + if (isChecked) + { + _carBaseSettings[carId].IgnoreLatestTimeToReachSocDateOnWeekend = false; + } + } + else if (checkboxName == "IgnoreLatestTimeToReachSocDateOnWeekend") + { + _carBaseSettings[carId].IgnoreLatestTimeToReachSocDateOnWeekend = isChecked; + if (isChecked) + { + _carBaseSettings[carId].IgnoreLatestTimeToReachSocDate = false; + } + } + } + + + public void Dispose() + { + _periodicTaskHelper?.Dispose(); + } + +} diff --git a/TeslaSolarCharger/Client/Program.cs b/TeslaSolarCharger/Client/Program.cs index ddbee862c..8e8d173d0 100644 --- a/TeslaSolarCharger/Client/Program.cs +++ b/TeslaSolarCharger/Client/Program.cs @@ -4,6 +4,8 @@ using MudBlazor.Services; using MudExtensions.Services; using TeslaSolarCharger.Client; +using TeslaSolarCharger.Client.Helper; +using TeslaSolarCharger.Client.Helper.Contracts; using TeslaSolarCharger.Shared; using TeslaSolarCharger.Shared.Contracts; using TeslaSolarCharger.Shared.Helper; @@ -18,6 +20,8 @@ //builder.Services.AddScoped(_ => new HttpClient { BaseAddress = new Uri("http://192.168.1.50:7190/") }); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddSharedDependencies(); builder.Services.AddMudServices(config => diff --git a/TeslaSolarCharger/Client/Shared/MainLayout.razor b/TeslaSolarCharger/Client/Shared/MainLayout.razor index 2b9a2a47f..5f0f55851 100644 --- a/TeslaSolarCharger/Client/Shared/MainLayout.razor +++ b/TeslaSolarCharger/Client/Shared/MainLayout.razor @@ -1,4 +1,10 @@ @inherits LayoutComponentBase +@using TeslaSolarCharger.Client.Helper +@implements IDisposable + +@inject IJSRuntime JsRuntime +@inject ISnackbar Snackbar +@inject HttpClient HttpClient { config.VisibleStateDuration = (int)reloadTimeout.TotalMilliseconds; }); + await Task.Delay(reloadTimeout); + await JsRuntime.InvokeVoidAsync("location.reload"); + } + + _version = version; + } + catch (Exception ex) + { + return; + } + + + } + + public void Dispose() + { + _periodicTaskHelper?.Dispose(); + } } diff --git a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj index 621e5a984..9c16febf7 100644 --- a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj +++ b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj @@ -24,9 +24,10 @@ - - - + + + + diff --git a/TeslaSolarCharger/Client/wwwroot/css/app.css b/TeslaSolarCharger/Client/wwwroot/css/app.css index 7d7b2da73..bba6cb08e 100644 --- a/TeslaSolarCharger/Client/wwwroot/css/app.css +++ b/TeslaSolarCharger/Client/wwwroot/css/app.css @@ -38,35 +38,6 @@ a, .btn-link { color: red; } -#blazor-error-ui { - background: lightyellow; - bottom: 0; - box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); - display: none; - left: 0; - padding: 0.6rem 1.25rem 0.7rem 1.25rem; - position: fixed; - width: 100%; - z-index: 1000; -} - - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } - -.blazor-error-boundary { - background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; - padding: 1rem 1rem 1rem 3.7rem; - color: white; -} - - .blazor-error-boundary::after { - content: "An error has occurred." - } - .loading-progress { position: relative; display: block; @@ -132,4 +103,22 @@ a, .btn-link { 100% { transform: rotate(360deg) } +} + +.material-symbols-outlined { + vertical-align: middle !important; +} + +@media (max-width: 640px) { + .auto-scroll { + overflow-x: scroll; + } +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; } \ No newline at end of file diff --git a/TeslaSolarCharger/Client/wwwroot/css/arrow-animation.css b/TeslaSolarCharger/Client/wwwroot/css/arrow-animation.css new file mode 100644 index 000000000..9a75ef653 --- /dev/null +++ b/TeslaSolarCharger/Client/wwwroot/css/arrow-animation.css @@ -0,0 +1,407 @@ +@media screen and (max-width: 640px) { + .right-arrow { + position: absolute; + margin-left: 23px; + width: 10px; + height: 10px; + border-top: 2px solid #000; + border-right: 2px solid #000; + margin-top: -4px; + transform: rotate(45deg); + } +} + +@media screen and (min-width: 641px) { + .right-arrow { + position: absolute; + margin-left: 50px; + width: 10px; + height: 10px; + border-top: 2px solid #000; + border-right: 2px solid #000; + margin-top: -4px; + transform: rotate(45deg); + } +} + +.clear { + clear: both; +} + +.power-container { + text-align: center; + max-width: 640px; + overflow: hidden; +} + +#solar-power, +#grid-power, +#battery-power, +#home-power, +#ev-station-power { + border: 1px solid #000; + padding: 5px; +} + +#grid-power, +#battery-power { + margin-top: 45px; +} + +#solar-to-home-line { + margin: auto; + height: 150px; + width: 3px; + color: #fff; + background-color: #000; +} + +.flow-div { + height: 50%; + width: 100%; +} + +#solar-to-grid-vertical-line { + float: right; + width: 2px; + height: 100%; + color: #fff; + background-color: #000; +} + +#solar-to-grid-horizontal-line { + width: 100%; + height: 2px; + color: #fff; + background-color: #000; + float: left; +} + +#grid-to-home-vertical-line { + width: 2px; + height: 100%; + color: #fff; + background-color: #000; + float: right; +} + +#grid-to-home-horizontal-line { + width: 100%; + height: 2px; + color: #fff; + background-color: #000; +} + +#solar-to-battery-vertical-line { + width: 2px; + height: 100%; + color: #fff; + background-color: #000; +} + +#solar-to-battery-horizontal-line { + width: 100%; + height: 2px; + color: #fff; + background-color: #000; +} + +#battery-to-home-vertical-line { + width: 2px; + height: 100%; + color: #fff; + background-color: #000; +} + +#battery-to-home-horizontal-line { + width: 100%; + height: 2px; + color: #fff; + background-color: #000; +} + +.battery-to-grid-horizontal-line { + width: 100%; + height: 2px; + color: #fff; + background-color: #000; + margin-bottom: 15px; +} + +#home-to-ev-station-vertical-line { + margin: auto; + width: 2px; + height: 40px; + color: #fff; + background-color: #000; +} + +.solar-to-grid-vertical-line-down-arrow, +.solar-to-battery-vertical-line-down-arrow, +.grid-to-home-vertical-line-down-arrow, +.battery-to-home-vertical-line-down-arrow{ + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid #000; + border-right: 2px solid #000; + transform: rotate(135deg); + margin-top: 65px; + margin-left: -3.9px; +} + +.home-to-ev-station-line-down-arrow { + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid #000; + border-right: 2px solid #000; + transform: rotate(135deg); + margin-top: 30px; + margin-left: -3.9px; +} + +.show { + visibility: visible; +} + +.hide { + visibility: hidden; +} + +.down-arrow { + position: absolute; + width: 10px; + height: 10px; + border-top: 3px solid #000; + border-right: 3px solid #000; + transform: rotate(135deg); + margin-top: 140px; + margin-left: -3.5px; +} + +.left-arrow { + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid #000; + border-right: 2px solid #000; + margin-top: -4px; + transform: rotate(-135deg); +} + +.leftArrowSliding { + position: absolute; + -webkit-animation: leftSlide 4s linear infinite; + animation: leftSlide 4s linear infinite; +} + +.rightArrowSliding { + position: absolute; + -webkit-animation: rightSlide 4s reverse infinite; + animation: rightSlide 4s reverse infinite; +} + +.downArrowSliding { + position: absolute; + -webkit-animation: downSlide 4s reverse infinite; + animation: downSlide 4s reverse infinite; +} + +.childDownArrowSliding { + position: absolute; + -webkit-animation: childDownSlide 4s reverse infinite; + animation: childDownSlide 4s reverse infinite; +} + + +.delay1 { + -webkit-animation-delay: 1s; + animation-delay: 1s; +} + +.delay2 { + -webkit-animation-delay: 2s; + animation-delay: 2s; +} + +.delay3 { + -webkit-animation-delay: 3s; + animation-delay: 3s; +} + +@-webkit-keyframes leftSlide { + 0% { + opacity: 0; + transform: translateX(30px); + } + + 20% { + opacity: 1; + transform: translateX(30px); + } + + 80% { + opacity: 1; + transform: translateX(-0px); + } + + 100% { + opacity: 0; + transform: translateX(-0px); + } +} + +@keyframes leftSlide { + 0% { + opacity: 0; + transform: translateX(30px); + } + + 20% { + opacity: 1; + transform: translateX(30px); + } + + 80% { + opacity: 1; + transform: translateX(-0px); + } + + 100% { + opacity: 0; + transform: translateX(-0px); + } +} + +@-webkit-keyframes rightSlide { + 0% { + opacity: 0; + transform: translateX(0px); + } + + 20% { + opacity: 1; + transform: translateX(0px); + } + + 60% { + opacity: 1; + transform: translateX(-30px); + } + + 100% { + opacity: 0; + transform: translateX(-30px); + } +} + +@keyframes rightSlide { + 0% { + opacity: 0; + transform: translateX(0px); + } + + 20% { + opacity: 1; + transform: translateX(0px); + } + + 60% { + opacity: 1; + transform: translateX(-30px); + } + + 100% { + opacity: 0; + transform: translateX(-30px); + } +} + +@-webkit-keyframes downSlide { + 0% { + opacity: 0; + transform: translateY(0px); + } + + 20% { + opacity: 1; + transform: translateY(0px); + } + + 80% { + opacity: 1; + transform: translateY(-30px); + } + + 100% { + opacity: 0; + transform: translateY(-100px); + } +} + +@keyframes downSlide { + 0% { + opacity: 0; + transform: translateY(0px); + } + + 20% { + opacity: 1; + transform: translateY(0px); + } + + 80% { + opacity: 1; + transform: translateY(-30px); + } + + 100% { + opacity: 0; + transform: translateY(-100px); + } +} + +@-webkit-keyframes childDownSlide { + 0% { + opacity: 0; + transform: translateY(0px); + } + + 20% { + opacity: 1; + transform: translateY(0px); + } + + 80% { + opacity: 1; + transform: translateY(-30px); + } + + 100% { + opacity: 0; + transform: translateY(-40px); + } +} + +@keyframes childDownSlide { + 0% { + opacity: 0; + transform: translateY(0px); + } + + 20% { + opacity: 1; + transform: translateY(0px); + } + + 80% { + opacity: 1; + transform: translateY(-30px); + } + + 100% { + opacity: 0; + transform: translateY(-40px); + } +} diff --git a/TeslaSolarCharger/Client/wwwroot/index.html b/TeslaSolarCharger/Client/wwwroot/index.html index 06dd8b2da..edb707476 100644 --- a/TeslaSolarCharger/Client/wwwroot/index.html +++ b/TeslaSolarCharger/Client/wwwroot/index.html @@ -6,10 +6,12 @@ TeslaSolarCharger + + @@ -39,6 +41,8 @@ + +