bassmaster187 / TeslaLogger

TeslaLogger is a self hosted data logger for your Tesla Model S/3/X/Y. Actually it supports RaspberryPi 3B, 3B+, 4B, Docker and Synology NAS.
https://www.teslalogger.de
GNU General Public License v3.0
517 stars 170 forks source link

Support for new wallbox "SmartEVSE 3" #1263

Open IVI053 opened 7 months ago

IVI053 commented 7 months ago

Is your feature request related to a problem? Please describe. no

Describe the solution you'd like Support for SmartEVSE 3 wallbox

Describe alternatives you've considered none

Additional context I use a SmartEVSE 3 controller in my wallbox build. The firmware from serkri (https://github.com/serkri/SmartEVSE-3) and dingo35 (https://github.com/dingo35/SmartEVSE-3.5) support getting data in json format. It woult be great if this could be supported in TL. I've already wrote some code that should work, but as I lack the possibility to configure a proper dev env for TL at the moment my code is not finally tested, but should work ;-)

Example JSON from SmartEVSE reachable under http://IP/settings

{
  "version": "SERKRI-1.8.0",
  "mode": "SOLAR",
  "mode_id": 2,
  "car_connected": true,
  "wifi": {
    "status": "WL_CONNECTED",
    "ssid": "xxxxx",
    "rssi": -13,
    "bssid": "01:23:45:67:89:AB"
  },
  "evse": {
    "temp": 21,
    "temp_max": 65,
    "connected": true,
    "access": true,
    "mode": 2,
    "loadbl": 0,
    "pwm": 1024,
    "solar_stop_timer": 0,
    "state": "Charging Stopped - Waiting for Solar",
    "state_id": 9,
    "error": "None",
    "error_id": 0,
    "rfid": "Not Installed"
  },
  "settings": {
    "charge_current": 100,
    "override_current": 0,
    "current_min": 6,
    "current_max": 16,
    "current_main": 25,
    "current_max_sum_mains": 600,
    "solar_max_import": 0,
    "solar_start_current": 12,
    "solar_stop_time": 15,
    "enable_C2": "Solar Off",
    "modem": "Not present",
    "mains_meter": "API",
    "starttime": 0,
    "stoptime": 0,
    "repeat": 0
  },
  "mqtt": {
    "host": "192.168.0.1",
    "port": 1883,
    "topic_prefix": "SmartEVSE/12345",
    "username": "",
    "password_set": 0,
    "status": "Connected"
  },
  "home_battery": {
    "current": -30,
    "last_update": 1712867312
  },
  "ev_meter": {
    "description": "Eastron3P",
    "address": 20,
    "import_active_power": 0,
    "total_kwh": 167.5,
    "charged_kwh": 0,
    "currents": {
      "TOTAL": 0,
      "L1": 0,
      "L2": 0,
      "L3": 0
    },
    "import_active_energy": 167.5,
    "export_active_energy": 0
  },
  "mains_meter": {
    "import_active_energy": 0,
    "export_active_energy": 0
  },
  "phase_currents": {
    "TOTAL": 31,
    "L1": 17,
    "L2": 0,
    "L3": 14,
    "last_data_update": 1712867310,
    "charging_L1": false,
    "charging_L2": false,
    "charging_L3": false,
    "original_data": {
      "TOTAL": 4,
      "L1": 8,
      "L2": -9,
      "L3": 5
    }
  },
  "backlight": {
    "timer": 0,
    "status": "OFF"
  }
}

ElectricityMeterSmartEVSE3.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.Caching;
using System.Text;
using System.Threading.Tasks;

using Exceptionless;
using Newtonsoft.Json;

namespace TeslaLogger
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Keine allgemeinen Ausnahmetypen abfangen", Justification = "<Pending>")]
    class ElectricityMeterSmartEVSE3 : ElectricityMeterBase
    {
        private string host;
        private string paramater;

        internal string mockup_status, mockup_shelly;

        Guid guid; // defaults to new Guid();
        static WebClient client;

        public ElectricityMeterSmartEVSE3(string host, string paramater)
        {
            this.host = host;
            this.paramater = paramater;

            if (client == null)
            {
                client = new WebClient();
            }
        }

        string GetCurrentData()
        {
            try
            {
                if (mockup_status != null)
                {
                    return mockup_status;
                }

                string cacheKey = "SmartEVSE3" + guid.ToString();
                object o = MemoryCache.Default.Get(cacheKey);

                if (o != null)
                {
                    return (string)o;
                }

                string url = host + "/settings";
                string lastJSON = client.DownloadString(url);

                MemoryCache.Default.Add(cacheKey, lastJSON, DateTime.Now.AddSeconds(10));
                return lastJSON;
            }
            catch (Exception ex)
            {
                if (ex is WebException wx)
                {
                    if ((wx.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound)
                    {
                        Logfile.Log(wx.Message);
                        return "";
                    }

                }
                if (!WebHelper.FilterNetworkoutage(ex))
                    ex.ToExceptionless().FirstCarUserID().Submit();

                Logfile.Log(ex.ToString());
            }

            return "";
        }

        public override double? GetUtilityMeterReading_kWh()
        {

            string j = null;
            try
            {
                j = GetCurrentData();

                if (string.IsNullOrEmpty(j))
                    return null;

                dynamic jsonResult = JsonConvert.DeserializeObject(j);

                string value = jsonResult["mains_meter"]["import_active_energy"];               

                return Double.Parse(value, Tools.ciEnUS);
            }
            catch (Exception ex)
            {
                ex.ToExceptionless().FirstCarUserID().Submit();
                Logfile.ExceptionWriter(ex, j);
            }

            return null;
        }

        public override double? GetVehicleMeterReading_kWh()
        {
            string j = null;
            try
            {
                j = GetCurrentData();

                if (string.IsNullOrEmpty(j))
                    return null;

                dynamic jsonResult = JsonConvert.DeserializeObject(j);

                string value = jsonResult["ev_meter"]["import_active_energy"];               

                return Double.Parse(value, Tools.ciEnUS);
            }
            catch (Exception ex)
            {
                ex.ToExceptionless().FirstCarUserID().Submit();
                Logfile.ExceptionWriter(ex, j);
            }

            return null;
        }

        public override bool? IsCharging()
        {
            string j = null;
            try
            {
                j = GetCurrentData();

                dynamic jsonResult = JsonConvert.DeserializeObject(j);
                if (jsonResult == null)
                    return null;

                bool car_connected = jsonResult["car_connected"];

                return car_connected;
            }
            catch (Exception ex)
            {
                ex.ToExceptionless().FirstCarUserID().Submit();
                Logfile.ExceptionWriter(ex, j);
            }

            return null;
        }

        public override string GetVersion()
        {
            string j = null;
            try
            {
                j = GetCurrentData();

                dynamic jsonResult = JsonConvert.DeserializeObject(j);
                if (jsonResult == null)
                    return null;

                string fwversion = jsonResult["version"];

                return fwversion;
            }
            catch (Exception ex)
            {
                if (!WebHelper.FilterNetworkoutage(ex))
                    ex.ToExceptionless().FirstCarUserID().Submit();

                Logfile.ExceptionWriter(ex, j);
            }

            return "";
        }
    }
}
bassmaster187 commented 7 months ago

Here are the docs how to setup a dev environment. Where do you have problems?

https://github.com/bassmaster187/TeslaLogger/blob/master/dev-howto.md

IVI053 commented 7 months ago

I have no Windows machine available to run Visual Studio 2022 on.