gickowtf / enpal-homeassistant

Home Assistant integration for Solar Installation from Enpal
MIT License
18 stars 5 forks source link

Fox ESS Inverter and Battery with different data declaration. #9

Open GetUpdate opened 4 months ago

GetUpdate commented 4 months ago

First at all, THE for the integration :-)

Today my ENPAL Solar system had been connected. FOX Ess Inverter and Battery plus ENPAL Box. The Inverter is connected via LAN cable with the Enpal Box. The Enpal Box is via LAN cable connected to my Switch. The Enpal Box is -up to now- in the same LAN (would like to put it later in to separate vLAN).

I could access the Box as explained in the Read.me, but I only get the one of the three cards (Enpal Energy Consumption , DAY, TOTAL...), I currently don't get the Battery nor the Inverter.

Via Data Collector (see belwo) the battery and inverter data is available, BUT the data fields ara partly with other device names or sensor names (inverter instead of sensor), Rename those within sensor.py

Block 1 Phases: Result: image

Changed:

Power Sensor -> Inverter

    if measurement == "inverter" and field == "Voltage.String.1":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage Phase A', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V'))
    if measurement == "inverter" and field == "Current.String.1":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere Phase A', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A'))
    if measurement == "inverter" and field == "Power.DC.String.1":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power Phase A', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    if measurement == "inverter" and field == "Voltage.String.2":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage Phase B', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V'))
    if measurement == "inverter" and field == "Current.String.2":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere Phase B', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A'))
    if measurement == "inverter" and field == "Power.DC.String.2":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power Phase B', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))        
    # if measurement == "powerSensor" and field == "Voltage.Phase.C":
    #    to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage Phase C', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V'))
    # if measurement == "powerSensor" and field == "Current.Phase.C":
    #    to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere Phase C', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A'))
    # if measurement == "powerSensor" and field == "Power.AC.Phase.C":
    #    to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power Phase C', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))

Block 2 Battery: Result: image

Changed (had problems with dev.class therefore deleted and just searched for the data field)

Battery

    if field == "Power.Battery.Charge.Discharge":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-charging', 'Enpal Battery Power', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    if field == "Energy.Battery.Charge.Level":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery', 'Enpal Battery Percent', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'battery', '%'))
    if field == "Energy.Battery.Charge.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-up', 'Enpal Battery Charge Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
    if field == "Energy.Battery.Discharge.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-down', 'Enpal Battery Discharge Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
    if field == "Energy.Battery.Charge.Load":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-up', 'Enpal Battery Charge Total', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

Kindly ask for your support / hints to get this more professional into your great Integration please

Kindest Regards M

Attached the view via Solar Rel. 8.33.1 Data Collector '{ "collectionId": "aafcafa4-1155-48de-b86c-4f3ccaa6dedb", "ioTDeviceId": "", "collectionType": "LiveValues", "timeStampUtc": "2024-03-06T16:36:21.0046923Z", "numberDataPoints": { "Power.Storage.Total": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9385405Z", "unit": "W", "value": -475.0 }, "Energy.Storage.Total.In.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:04.8880897Z", "unit": "kWh", "value": 2.1 }, "Energy.Storage.Total.Out.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:04.888126Z", "unit": "kWh", "value": 3.6 }, "Energy.Storage.Level": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9391969Z", "unit": "Wh", "value": 1614.6000000000001 }, "Percent.Storage.Level": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:21.0045407Z", "unit": "Percent", "value": 3.3333333333333335 }, "Power.Production.Total": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9388277Z", "unit": "W", "value": 121.0 }, "Energy.Production.Total.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:06.9827422Z", "unit": "kWh", "value": 2.6 }, "Power.External.Total": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9388336Z", "unit": "W", "value": -47.0 }, "Energy.External.Total.Out.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:04.888149Z", "unit": "kWh", "value": 0.1 }, "Energy.External.Total.In.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:04.888227Z", "unit": "kWh", "value": 1.4 }, "Energy.Consumption.Total.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:20.0746153Z", "unit": "kWh", "value": 3.0 }, "Power.Consumption.Total": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9397455Z", "unit": "W", "value": 643.0 } }, "textDataPoints": {}, "errorCodes": [], "DeviceCollections": [ { "deviceId": "15d04345-8d9a-43f7-a7b3-bba51697a811", "deviceClass": "Wallbox", "timeStampUtc": "2024-03-06T16:36:21.0047227Z", "numberDataPoints": { "Power.Wallbox.Connector.0.Charging.Requested": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:20.9996624Z", "unit": "W", "value": 4500.0 }, "Power.Wallbox.Connector.1.Charging.Requested": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:20.9996663Z", "unit": "W", "value": 4500.0 }, "Status.Wallbox.Connected": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:20.9996669Z", "unit": "None", "value": 0.0 } }, "textDataPoints": { "Wallbox.DeviceId": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:20.9996676Z", "unit": "None", "value": "" } }, "errorCodes": [] }, { "deviceId": "ac2ad07d-2c24-492c-a347-9e1aa4cfa518", "deviceClass": "IoTEdgeDevice", "timeStampUtc": "2024-03-06T16:36:21.0047617Z", "numberDataPoints": { "LTE.RSRP": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15Z", "unit": "None", "value": -102.0 }, "LTE.RSRQ": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15Z", "unit": "None", "value": -10.0 }, "LTE.RSSI": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15Z", "unit": "None", "value": -77.0 }, "LTE.SNR": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15Z", "unit": "None", "value": 11.0 }, "LTE.Quality": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15Z", "unit": "Percent", "value": 59.0 }, "LTE.Predictor.Result.Passed": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:21.0025447Z", "unit": "None", "value": 1.0 }, "Cpu.Load": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:05Z", "unit": "Percent", "value": 24.0 }, "Memory.Usage": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:05Z", "unit": "Percent", "value": 55.0 } }, "textDataPoints": { "IoT.MainState": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:20.9996896Z", "unit": "None", "value": "Installation" }, "LTE.CellularGuard.Result.Timestamp": { "timeStampUtcOfMeasurement": "2024-03-06T16:24:13Z", "unit": "None", "value": "2024-03-06T16:24:13Z" }, "LTE.CellularGuard.Result.Version": { "timeStampUtcOfMeasurement": "2024-03-06T16:24:13Z", "unit": "None", "value": "v6.3" }, "LTE.CellularGuard.Result.Value": { "timeStampUtcOfMeasurement": "2024-03-06T16:24:13Z", "unit": "None", "value": "cg.ok" }, "LTE.Cronny.Result": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15Z", "unit": "None", "value": "cronny.lte_signal.weak" }, "LTE.State": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15Z", "unit": "None", "value": "connected" }, "LTE.Connection.Type": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15Z", "unit": "None", "value": "LTE" }, "LTE.Modem.Type": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15Z", "unit": "None", "value": "quectel_ec21eux" }, "LTE.Modem.Firmware.Version": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15Z", "unit": "None", "value": "EC21EUXGAR08A07M1G_20.200.20.200" }, "HW.Cronny.Result": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:05Z", "unit": "None", "value": "cronny.hw_metrics.ok" }, "LTE.Failover.Result": { "timeStampUtcOfMeasurement": "2024-03-06T16:35:00Z", "unit": "None", "value": "cronny.lte_failover.lan_ok" }, "LTE.Fail-over.Message.0": { "timeStampUtcOfMeasurement": "2024-03-06T16:35:38Z", "unit": "None", "value": "current connection: eth1" }, "LTE.Fail-over.Message.1": { "timeStampUtcOfMeasurement": "2024-03-06T16:35:52Z", "unit": "None", "value": "eth1 is acceptable latency: 0.55425, errors: 0" }, "LTE.Fail-over.Message.2": { "timeStampUtcOfMeasurement": "2024-03-06T16:35:52Z", "unit": "None", "value": "current connection is good, staying on eth1" } }, "errorCodes": [] }, { "deviceId": "9a54c3ea-bf16-40e9-8c51-bd48b6480869", "deviceClass": "Battery", "timeStampUtc": "2024-03-06T16:36:21.0048423Z", "numberDataPoints": { "Mode.Forcible.Timeout": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.4161067Z", "unit": "None", "value": 0.0 }, "Power.Battery.Charge.Discharge": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9385405Z", "unit": "W", "value": -475.0 }, "Energy.Battery.Charge.Load": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9391969Z", "unit": "Wh", "value": 1614.6000000000001 }, "Power.Battery.Charge.Discharge.Set": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:18.5085443Z", "unit": "W", "value": 0.0 }, "Mode.Forcible.Charge.Discharge": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:20.599429Z", "unit": "None", "value": 0.0 }, "Voltage.Battery": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:11.691463Z", "unit": "V", "value": 172.9 }, "Current.Battery": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:11.6914727Z", "unit": "A", "value": 2.7 }, "Energy.Battery.Charge.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:04.8880897Z", "unit": "kWh", "value": 2.1 }, "Energy.Battery.Discharge.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:04.888126Z", "unit": "kWh", "value": 3.6 }, "Battery.SOH": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:06.4597381Z", "unit": "Percent", "value": 100.0 }, "Energy.Battery.Ah": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15.3682438Z", "unit": "None", "value": 69.0 }, "Battery.ChargeLevel.Min": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:13.2600391Z", "unit": "Percent", "value": 10.0 }, "Battery.ChargeLevel.Max": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:13.7791188Z", "unit": "Percent", "value": 100.0 }, "Battery.ChargeLevel.MinOnGrid": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:14.3031146Z", "unit": "Percent", "value": 10.0 }, "Energy.Battery.Charge.Level": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:21.0045407Z", "unit": "Percent", "value": 3.3333333333333335 } }, "textDataPoints": { "Battery.Model.Code": { "timeStampUtcOfMeasurement": "2024-03-06T14:34:34.1105756Z", "unit": "None", "value": "ECS4300" } }, "errorCodes": [] }, { "deviceId": "a75a0173-d2a9-463a-a029-052a4aaaebb1", "deviceClass": "Inverter", "timeStampUtc": "2024-03-06T16:36:21.0048925Z", "numberDataPoints": { "Energy.Grid.Export.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:04.888149Z", "unit": "kWh", "value": 0.1 }, "Energy.Grid.Import.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:04.888227Z", "unit": "kWh", "value": 1.4 }, "Energy.Production.Total.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:06.9827422Z", "unit": "kWh", "value": 2.6 }, "Voltage.String.1": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:07.5071515Z", "unit": "V", "value": 674.1 }, "Current.String.1": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:07.5071816Z", "unit": "A", "value": 0.0 }, "Power.DC.String.1": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:07.5071856Z", "unit": "W", "value": 67.0 }, "Voltage.String.2": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:07.5071891Z", "unit": "V", "value": 416.2 }, "Current.String.2": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:07.507192Z", "unit": "A", "value": 0.1 }, "Power.DC.String.2": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:07.5071976Z", "unit": "W", "value": 52.0 }, "Frequency.Grid": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:09.0749769Z", "unit": "Hz", "value": 50.0 }, "Command.Start.Stop": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:10.647852Z", "unit": "None", "value": 1.0 }, "Dongle.BackendConnection": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15.3684889Z", "unit": "None", "value": 1.0 }, "Inverter.System.State": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9385905Z", "unit": "None", "value": 2.0 }, "Voltage.Phase.A": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9386985Z", "unit": "V", "value": 234.1 }, "Voltage.Phase.B": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9387025Z", "unit": "V", "value": 235.3 }, "Voltage.Phase.C": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9387059Z", "unit": "V", "value": 235.3 }, "Power.DC.Total": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9388277Z", "unit": "W", "value": 121.0 }, "Power.Grid.Export": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9388336Z", "unit": "W", "value": -47.0 }, "Power.House.Total": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9397455Z", "unit": "W", "value": 643.0 }, "Power.Limit": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:17.9832924Z", "unit": "Percent", "value": 100.0 }, "Energy.Consumption.Total.Day": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:20.0746153Z", "unit": "kWh", "value": 3.0 }, "Mode.Forcible.Charge.Discharge": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:20.599429Z", "unit": "None", "value": 0.0 } }, "textDataPoints": {}, "errorCodes": [] }, { "deviceId": "83facf0f-35ff-4d3d-afe8-b86de47d3f22", "deviceClass": "PowerSensor", "timeStampUtc": "2024-03-06T16:36:21.0049552Z", "numberDataPoints": { "Power.AC.Phase.A": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:05.9366111Z", "unit": "W", "value": -45.0 }, "Power.AC.Phase.B": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:05.9366236Z", "unit": "W", "value": 162.0 }, "Power.AC.Phase.C": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:05.9366321Z", "unit": "W", "value": -77.0 }, "Power.Factor": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:06.4595887Z", "unit": "Percent", "value": -0.05 }, "Power.Reactive": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:09.5993263Z", "unit": "kW", "value": 469.0 }, "Meter.Enable.Disable": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:12.7362465Z", "unit": "None", "value": 0.0 }, "Meter2.AC.Phase.A": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15.3684354Z", "unit": "W", "value": 0.0 }, "Meter2.AC.Phase.B": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15.3684487Z", "unit": "W", "value": 0.0 }, "Meter2.AC.Phase.C": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15.3684644Z", "unit": "W", "value": 0.0 }, "Meter2.Enable.Disable": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:15.8921872Z", "unit": "None", "value": 0.0 }, "Meter.Connect.State": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:16.9386066Z", "unit": "None", "value": 1.0 }, "Meter2.Connect.State": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:17.4602924Z", "unit": "None", "value": 0.0 }, "Meter2.AC.Total": { "timeStampUtcOfMeasurement": "2024-03-06T16:36:19.5554592Z", "unit": "W", "value": 0.0 } }, "textDataPoints": {}, "errorCodes": [] } ], "EnergyManagement": [ { "energyManagementId": "17e99b63-439e-43ab-a6ce-7cb2444e93ce", "referenceDeviceId": "15d04345-8d9a-43f7-a7b3-bba51697a811", "timeStampUtc": "2024-03-06T16:36:21.0061354Z", "numberDataPoints": {}, "textDataPoints": {}, "errorCodes": [] } ] }

SvenAbels commented 3 months ago

I got the same problem here with my new fox ESS. I'm trying to integrate your changes but I'm not sure where. Do I understand you right, that you modified the original sensor.py file from the extension by simply editing the file from the custom_components\enpal\ folder?

@GetUpdate Maybe you can attach your sensor.py file here?

@gickowtf Of course it would also be fantactic if it could be added to the official extension

SvenAbels commented 3 months ago

@GetUpdate I managed to integrate it succesfully. But I believe that the value with the String 1 and Sring 2 is wrong in your script above: They do not represent the phases but the Watt amount of the 2 PV strings connected to the Enpal system

gickowtf commented 3 months ago

i have published a prerelease v0.2.1 https://github.com/gickowtf/enpal-homeassistant/releases/tag/0.2.1

gickowtf commented 3 months ago

hey ich glaube wir können, da es um enpal geht, auch deutsch schreiben ;D ich habe gerade eben mal ein pre release rein gestellt... habe ihr Zugang zu influxdb?? könnt ihr mir die genauen fields nennen. dann kann ich nochmal ein vernünftiges update machen.

SvenAbels commented 3 months ago

hey ich glaube wir können, da es um enpal geht, auch deutsch schreiben ;D ich habe gerade eben mal ein pre release rein gestellt...

Klasse, vielen Dank. :-)

habe ihr Zugang zu influxdb?? könnt ihr mir die genauen fields nennen. dann kann ich nochmal ein vernünftiges update machen.

Leider habe ich keinen Zugang zur Influxdb. Ich hab die Konfigurationsbeispiele oben etwas abgeändert, indem ich nicht die Phasen A/B/C, sondern die Strings abgefragt habe und mir ausgeben lasse. Das klappt super:

#Power Sensor -> Inverter if measurement == "inverter" and field == "Voltage.String.1": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage String 1', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V')) if measurement == "inverter" and field == "Current.String.1": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere String 1', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A')) if measurement == "inverter" and field == "Power.DC.String.1": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power String 1', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W')) if measurement == "inverter" and field == "Voltage.String.2": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage String 2', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V')) if measurement == "inverter" and field == "Current.String.2": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere String 2', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A')) if measurement == "inverter" and field == "Power.DC.String.2": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power String 2', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))

Ich bekomme aber bei meiner FoxESS Anlage leider keinen Wert für 'Enpal Energy External In Day' (bzw. immer 0 kwh), obwohl für 'Enpal Energy External Out Day' alles timmt. Ich vermute aber, dass das kein Problem deines Skriptes ist, denn in dem Enpal Solar Rel wird der Wert ebenfalls mit 0 aufgeführt (anders als in der App).

Was ich noch nicht verstehe ist die Bedeutung von "Enpal Energy Consumption". Was verbirgt sich dahinter?

Viele Grüße und ein frohes Osterfest :-),

Sven

SvenAbels commented 3 months ago

Hi,

ein kurzer Nachtrag: Ich habe die if measurement == "..." statements nun mit der Ausnahme vom Power-Abschnitt ganz bei mir rausgenommen, da die Werte andernfalls ab und zu nicht übertragen wurden. Nun läuft alles stabil mit der FoxESS - bzw. zumindest mit den Werten, die ich auch im Solar Rel sehe.

skehrer commented 3 months ago

hey ich glaube wir können, da es um enpal geht, auch deutsch schreiben ;D ich habe gerade eben mal ein pre release rein gestellt...

Klasse, vielen Dank. :-)

habe ihr Zugang zu influxdb?? könnt ihr mir die genauen fields nennen. dann kann ich nochmal ein vernünftiges update machen.

Leider habe ich keinen Zugang zur Influxdb. Ich hab die Konfigurationsbeispiele oben etwas abgeändert, indem ich nicht die Phasen A/B/C, sondern die Strings abgefragt habe und mir ausgeben lasse. Das klappt super:

#Power Sensor -> Inverter if measurement == "inverter" and field == "Voltage.String.1": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage String 1', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V')) if measurement == "inverter" and field == "Current.String.1": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere String 1', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A')) if measurement == "inverter" and field == "Power.DC.String.1": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power String 1', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W')) if measurement == "inverter" and field == "Voltage.String.2": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage String 2', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V')) if measurement == "inverter" and field == "Current.String.2": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere String 2', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A')) if measurement == "inverter" and field == "Power.DC.String.2": to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power String 2', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))

Ich bekomme aber bei meiner FoxESS Anlage leider keinen Wert für 'Enpal Energy External In Day' (bzw. immer 0 kwh), obwohl für 'Enpal Energy External Out Day' alles timmt. Ich vermute aber, dass das kein Problem deines Skriptes ist, denn in dem Enpal Solar Rel wird der Wert ebenfalls mit 0 aufgeführt (anders als in der App).

Was ich noch nicht verstehe ist die Bedeutung von "Enpal Energy Consumption". Was verbirgt sich dahinter?

Viele Grüße und ein frohes Osterfest :-),

Sven

Hi Sven,

Ich habe in meiner Übersicht auch meistens für die "Enpal Energy Consumption" 0kWh stehen. Aktuell steht der Wert auf 0,5kWh. So wie ich das verstehe ist das der Gesamtbezug deines Hauses aus dem Netz. Wenn Du also ein System mit Speicher hast der über die Nacht reicht und Du momentan mehr produzierst als Du verbrauchst macht es Sinn, dass da eine 0 steht. Das ist bei mir der Fall, es schient aber so als ob über den Tag verteilt eine gewisse Minimalmenge immer aus dem Netz bezogen wird. Ich habe seit der Installation der Anlage vor ca 2 Wochen im Schnitt einen täglichen Bezug aus dem Netz con ca 0,5kWh gehabt.

Gruß, Stephan

SvenAbels commented 3 months ago

Hi Stephan,

ich habe noch einen zweiten Smart Meter von der Wallbox und auch den Zähler etwas beobachtet. Es scheint so als wenn der Gesamtverbrauch aus dem Netz immer "Enpal Energy Consumption" + " Energy External In Day" entspricht. Bei mir sind beispielsweise heute beide Werte gefüllt - einmal mit 1,1kwh und einmal mit 0,2kwh. Der korrekte Wert lauf meinem SmartMeter der Wallbox liegt bei 1.3...

skehrer commented 3 months ago

hey ich glaube wir können, da es um enpal geht, auch deutsch schreiben ;D ich habe gerade eben mal ein pre release rein gestellt... habe ihr Zugang zu influxdb?? könnt ihr mir die genauen fields nennen. dann kann ich nochmal ein vernünftiges update machen.

Hi Gicko,

Ich habe seit knapp 2 Wochen ebenfalls ein System von Enpal mit Inverter und 20 kWh Speicher vonFox ESS. Heute habe ich auch den "viewer"-Zugang von Enpal erhalten und mir Deine Integration gleich mal installiert und angeschaut. Vielen Dank für die Arbeit die Du da investiert hast.

Bei mir funktionieren ebenfalls momentan einige der Sensoren nicht. Ich sehe mit der aktuellen Version der Integration folgendes:

Was mir nicht angezeigt wird sind sämtliche Batterie bezogenen Werte sowie die Enpal Voltage Phase A-C, Enpal Power Phase A-C, Enpal Ampere Phase A-C und die Werte zur Wallbox (habe ich keine).

Auf die Schnelle musste ich feststellen, dass bei mir die relevanten measurements teilweise anders heißen als in Deiner Integration. Ich habe zum Beispiel kein measurement namens "battery". Bei mir stehen die entsprechenden Felder zum Großteil im measurement "inverter" und (teilweise gedoppelt und mit anderem Feldbezeichner) im measurement "system".

Ein schneller Test bei dem ich das measurement für die Batteriegruppe durch "inverter" ersetzt habe hat dafür gesorgt, dass ich jetzt die entsprechenden Batteriesensoren angezeigt bekomme. Ich werde mir das in den nächsten Tagen mal genauer anschauen und Dir dann meinen Fix zukommen lassen. Ich kann Dir auch gerne das genaue Schema der influxDB die auf meinem System läuft (Solar Rel 8.35) zukommen lassen wenn Du mir sagst, auf welchem Weg ich das am besten machen soll.

Als weitere kurze Information habe ich vom Enpal Service-Team noch folgende Aussage bekommen: "Wir arbeiten in diesem Jahr an einer größeren Umstrukturierung unserer Lösung die dann auch auf Ihrem System ausgerollt wird. Dies wird dann auf jeden Fall auch die Influx DB betreffen. Sie müssten dann noch einmal Anpassungen an Ihrem System vornehmen."

Nur als kleine Warnung vorab, dass zumindest bei den neuen Enpal-Boxen wohl in absehbarer Zukunft noch das eine oder andere an Anpassungen fällig werden wird.

Gruß, Stephan

GetUpdate commented 3 months ago

hey ich glaube wir können, da es um enpal geht, auch deutsch schreiben ;D ich habe gerade eben mal ein pre release rein gestellt... habe ihr Zugang zu influxdb?? könnt ihr mir die genauen fields nennen. dann kann ich nochmal ein vernünftiges update machen.

Hi Gicko,

Ich habe seit knapp 2 Wochen ebenfalls ein System von Enpal mit Inverter und 20 kWh Speicher vonFox ESS. Heute habe ich auch den "viewer"-Zugang von Enpal erhalten und mir Deine Integration gleich mal installiert und angeschaut. Vielen Dank für die Arbeit die Du da investiert hast.

Bei mir funktionieren ebenfalls momentan einige der Sensoren nicht. Ich sehe mit der aktuellen Version der Integration folgendes:

  • Enpal Energy Consumption
  • Enpal Energy External In Day
  • Enpal Energy External Out Day
  • Enpal Power External Total
  • Enpal Power House Total
  • Enpal Production Day
  • Enpal Solar Production Power

Was mir nicht angezeigt wird sind sämtliche Batterie bezogenen Werte sowie die Enpal Voltage Phase A-C, Enpal Power Phase A-C, Enpal Ampere Phase A-C und die Werte zur Wallbox (habe ich keine).

Auf die Schnelle musste ich feststellen, dass bei mir die relevanten measurements teilweise anders heißen als in Deiner Integration. Ich habe zum Beispiel kein measurement namens "battery". Bei mir stehen die entsprechenden Felder zum Großteil im measurement "inverter" und (teilweise gedoppelt und mit anderem Feldbezeichner) im measurement "system".

Ein schneller Test bei dem ich das measurement für die Batteriegruppe durch "inverter" ersetzt habe hat dafür gesorgt, dass ich jetzt die entsprechenden Batteriesensoren angezeigt bekomme. Ich werde mir das in den nächsten Tagen mal genauer anschauen und Dir dann meinen Fix zukommen lassen. Ich kann Dir auch gerne das genaue Schema der influxDB die auf meinem System läuft (Solar Rel 8.35) zukommen lassen wenn Du mir sagst, auf welchem Weg ich das am besten machen soll.

Als weitere kurze Information habe ich vom Enpal Service-Team noch folgende Aussage bekommen: "Wir arbeiten in diesem Jahr an einer größeren Umstrukturierung unserer Lösung die dann auch auf Ihrem System ausgerollt wird. Dies wird dann auf jeden Fall auch die Influx DB betreffen. Sie müssten dann noch einmal Anpassungen an Ihrem System vornehmen."

Nur als kleine Warnung vorab, dass zumindest bei den neuen Enpal-Boxen wohl in absehbarer Zukunft noch das eine oder andere an Anpassungen fällig werden wird.

Gruß, Stephan

Hallo, Sorry für die späte Rückmeldung, war 2 Wochen am Roten Meer und leider ist dort kein VPN möglich... Sehe mit der V02.2 auch wie @skehrer nur die Enitäten wie oben beschrieben.

Mir fehlt mit V0.2.2 noch immer die Werte wie ich oben angegeben habe (Battery und Phases). Mit meiner "abgeleiteten" Sensor.py klappt das jedoch auch mit der V02.2. wieder.

@skehrer Du kannst ja mal versuchen ob das dann bei Dir auch klappt. Die zu editierende Datei findest Du unter /homeassistant/custom_components/enpal/sensor.py -> Umbenennen und als Kopie behalten -> Inhalt von sensor.py mit werten unten überschreiben. -> HA neu starten...

Gruß Michael

Sensor.py:

"""Platform for sensor integration.""" from future import annotations

import asyncio import uuid from datetime import timedelta, datetime from homeassistant.components.sensor import (SensorEntity) from homeassistant.core import HomeAssistant from homeassistant import config_entries from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_registry import async_get, async_entries_for_config_entry from custom_components.enpal.const import DOMAIN import aiohttp import logging from influxdb_client import InfluxDBClient

_LOGGER = logging.getLogger(name) SCAN_INTERVAL = timedelta(seconds=20)

VERSION= '0.1.0'

def get_tables(ip: str, port: int, token: str): client = InfluxDBClient(url=f'http://{ip}:{port}', token=token, org='enpal') query_api = client.query_api()

query = 'from(bucket: "solar") \
  |> range(start: -5m) \
  |> last()'

tables = query_api.query(query)
return tables

async def async_setup_entry( hass: HomeAssistant, config_entry: config_entries.ConfigEntry, async_add_entities, ):

Get the config entry for the integration

config = hass.data[DOMAIN][config_entry.entry_id]
if config_entry.options:
    config.update(config_entry.options)
to_add = []
if not 'enpal_host_ip' in config:
    _LOGGER.error("No enpal_host_ip in config entry")
    return
if not 'enpal_host_port' in config:
    _LOGGER.error("No enpal_host_port in config entry")
    return
if not 'enpal_token' in config:
    _LOGGER.error("No enpal_token in config entry")
    return

global_config = hass.data[DOMAIN]

tables = await hass.async_add_executor_job(get_tables, config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'])

for table in tables:
    field = table.records[0].values['_field']
    measurement = table.records[0].values['_measurement']

    if measurement == "inverter" and field == "Power.DC.Total":
        to_add.append(EnpalSensor(field, measurement, 'mdi:solar-power', 'Enpal Solar Production Power', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    if measurement == "inverter" and field == "Power.House.Total":
        to_add.append(EnpalSensor(field, measurement, 'mdi:home-lightning-bolt', 'Enpal Power House Total', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    if measurement == "system" and field == "Power.External.Total":
        to_add.append(EnpalSensor(field, measurement, 'mdi:home-lightning-bolt', 'Enpal Power External Total', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    # Consum Total per Day
    if measurement == "system" and field == "Energy.Consumption.Total.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:home-lightning-bolt', 'Enpal Energy Consumption', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

    # to the Grid and from the Grid
    if measurement == "system" and field == "Energy.External.Total.Out.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:transmission-tower-export', 'Enpal Energy External Out Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
    if measurement == "system" and field == "Energy.External.Total.In.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:transmission-tower-import', 'Enpal Energy External In Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

    # Solar Energy.Production.Total.Day
    if measurement == "system" and field == "Energy.Production.Total.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:solar-power-variant', 'Enpal Production Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

    #Battery    
    if field == "Power.Battery.Charge.Discharge":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-charging', 'Enpal Battery Power', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    if field == "Energy.Battery.Charge.Level":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery', 'Enpal Battery Percent', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'battery', '%'))
    if field == "Energy.Battery.Charge.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-up', 'Enpal Battery Charge Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
    if field == "Energy.Battery.Discharge.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-down', 'Enpal Battery Discharge Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
    if field == "Energy.Battery.Charge.Load":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-up', 'Enpal Battery Charge Total', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

    #Power Sensor -> Inverter
    if measurement == "inverter" and field == "Voltage.String.1":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage Phase A', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V'))
    if measurement == "inverter" and field == "Current.String.1":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere Phase A', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A'))
    if measurement == "inverter" and field == "Power.DC.String.1":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power Phase A', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    if measurement == "inverter" and field == "Voltage.String.2":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage Phase B', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V'))
    if measurement == "inverter" and field == "Current.String.2":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere Phase B', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A'))
    if measurement == "inverter" and field == "Power.DC.String.2":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power Phase B', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))        

entity_registry = async_get(hass)
entries = async_entries_for_config_entry(
    entity_registry, config_entry.entry_id
)
for entry in entries:
    entity_registry.async_remove(entry.entity_id)

async_add_entities(to_add, update_before_add=True)

class EnpalSensor(SensorEntity):

def __init__(self, field: str, measurement: str, icon:str, name: str, ip: str, port: int, token: str, device_class: str, unit: str):
    self.field = field
    self.measurement = measurement
    self.ip = ip
    self.port = port
    self.token = token
    self.enpal_device_class = device_class
    self.unit = unit
    self._attr_icon = icon
    self._attr_name = name
    self._attr_unique_id = f'enpal_{measurement}_{field}'
    self._attr_extra_state_attributes = {}

async def async_update(self) -> None:

    # Get the IP address from the API
    try:
        client = InfluxDBClient(url=f'http://{self.ip}:{self.port}', token=self.token, org="enpal")
        query_api = client.query_api()

        query = f'from(bucket: "solar") \
          |> range(start: -5m) \
          |> filter(fn: (r) => r["_measurement"] == "{self.measurement}") \
          |> filter(fn: (r) => r["_field"] == "{self.field}") \
          |> last()'

        tables = await self.hass.async_add_executor_job(query_api.query, query)

        value = 0
        if tables:
            value = tables[0].records[0].values['_value']

        self._attr_native_value = round(float(value), 2)
        self._attr_device_class = self.enpal_device_class
        self._attr_native_unit_of_measurement   = self.unit
        self._attr_state_class = 'measurement'
        self._attr_extra_state_attributes['last_check'] = datetime.now()
        self._attr_extra_state_attributes['field'] = self.field
        self._attr_extra_state_attributes['measurement'] = self.measurement

        #if self.field == 'Energy.Consumption.Total.Day' or 'Energy.Storage.Total.Out.Day' or 'Energy.Storage.Total.In.Day' or 'Energy.Production.Total.Day' or 'Energy.External.Total.Out.Day' or 'Energy.External.Total.In.Day':
        if self._attr_native_unit_of_measurement == "kWh":
            self._attr_extra_state_attributes['last_reset'] = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
            self._attr_state_class = 'total'
        if self._attr_native_unit_of_measurement == "Wh":
            self._attr_extra_state_attributes['last_reset'] = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
            self._attr_state_class = 'total'

        if self.field == 'Percent.Storage.Level':
            if self._attr_native_value >= 10:
                self._attr_icon = "mdi:battery-outline"
            if self._attr_native_value <= 19 and self._attr_native_value >= 10:
                self._attr_icon = "mdi:battery-10"
            if self._attr_native_value <= 29 and self._attr_native_value >= 20:
                self._attr_icon = "mdi:battery-20"
            if self._attr_native_value <= 39 and self._attr_native_value >= 30:
                self._attr_icon = "mdi:battery-30"
            if self._attr_native_value <= 49 and self._attr_native_value >= 40:
                self._attr_icon = "mdi:battery-40"
            if self._attr_native_value <= 59 and self._attr_native_value >= 50:
                self._attr_icon = "mdi:battery-50"    
            if self._attr_native_value <= 69 and self._attr_native_value >= 60:
                self._attr_icon = "mdi:battery-60"
            if self._attr_native_value <= 79 and self._attr_native_value >= 70:
                self._attr_icon = "mdi:battery-70"
            if self._attr_native_value <= 89 and self._attr_native_value >= 80:
                self._attr_icon = "mdi:battery-80"
            if self._attr_native_value <= 99 and self._attr_native_value >= 90:
                self._attr_icon = "mdi:battery-90"        
            if self._attr_native_value == 100:
                self._attr_icon = "mdi:battery"

    except Exception as e:
        _LOGGER.error(f'{e}')
        self._state = 'Error'
        self._attr_native_value = None
        self._attr_extra_state_attributes['last_check'] = datetime.now()

Führt dann zu 18 Entitäten: image

skehrer commented 2 months ago

Hi Gicko,

Danke für die angepasste Sensor.py. Ich teste morgen (sofern ich dazu komme) mal aus ob das bei mir funktioniert. Ich habe mir aber die statements gerade mal angeschaut und denke das müsste passen. Gibt es einen Grund warum Du bei den Sensoren für die Batterie das measurement aus der if-Abfrage rausgenommen hast? Ich hätte eher mit sowas gerechnet:

        if measurement == "inverter" and field == "Power.Battery.Charge.Discharge":
            to_add.append(EnpalSensor(field, measurement, 'mdi:battery-charging', 'Enpal Battery Power', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))

Was ich heute abend mal gemacht habe ist mir den aktuellen Aufbau des solar-Buckets anzuschauen. In der aktuellen Version (immer noch Solar Rel 8.35 vom 25.03.2024) habe ich im measurement "inverter" etliche zusätzliche Felder gegenüber dem letzten Mal als ich mir das angeschaut habe. Das Schema sieht bei mir aktuell so aus:

Measurement "System":

Energy.Consumption.Total.Day
Energy.External.Total.In.Day
Energy.External.Total.Out.Day
Energy.Production.Total.Day
Energy.Storage.Level
Energy.Storage.Total.In.Day
Energy.Storage.Total.Out.Day
Percent.Storage.Level
Power.Consumption.Total
Power.External.Total
Power.Production.Total
Power.Storage.Total
measureId

Measurement "inverter":

Battery.ChargeLevel.Max
Battery.ChargeLevel.Min
Battery.ChargeLevel.MinOnGrid
Battery.SOH
Command.Start.Stop
Current.Battery
Current.String.1
Current.String.2
Dongle.BackendConnection
Energy.Battery.Ah
Energy.Battery.Charge.Day
Energy.Battery.Charge.Level
Energy.Battery.Charge.Load
Energy.Battery.Discharge.Day
Energy.Consumption.Total.Day
Energy.Grid.Export.Day
Energy.Grid.Import.Day
Energy.Production.Total.Day
Frequency.Grid
Inverter.System.State
Meter.Connect.State
Meter.Enable.Disable
Meter2.AC.Phase.A
Meter2.AC.Phase.B
Meter2.AC.Phase.C
Meter2.AC.Total
Meter2.Connect.State
Meter2.Enable.Disable
Mode.Forcible.Charge.Discharge
Mode.Forcible.Timeout
Power.AC.Phase.A
Power.AC.Phase.B
Power.AC.Phase.C
Power.Battery.Charge.Discharge
Power.Battery.Charge.Discharge.Set
Power.DC.String.1
Power.DC.String.2
Power.DC.Total
Power.Factor
Power.Grid.Export
Power.House.Total
Power.Limit
Power.Reactive
Voltage.Battery
Voltage.Phase.A
Voltage.Phase.B
Voltage.Phase.C
Voltage.String.1
Voltage.String.2

Measurement "iot":

Cpu.Load
LTE.Predictor.Result.Passed
LTE.Quality
LTE.RSRP
LTE.RSRQ
LTE.RSSI
LTE.SNR
Memory.Usage

Measurement "wallbox":

Power.Wallbox.Connector.0.Charging.Requested
Power.Wallbox.Connector.1.Charging.Requested
Status.Wallbox.Connected

Einige Felder scheinen dabei Dopplungen zu sein, z.B. "system-Energy.Storage.Total.In.Day" und "inverter-Energy.Battery.Charge.Day", die beide "Enpal Battery Charge Day" anzuzeigen scheinen.

Ich nehme mir in den nächsten Tagen mal Deine originale Sensor.py und passe sie so an, dass sie für mein System passt. Wenn ich das gemacht habe stelle ich sie Dir hier in einen Post.

Gruß, Stephan

TheFlea80 commented 2 months ago

Da ich noch ein Paar Probleme mit dem Energy Dashboard hatte, habe ich mir die Config und die Daten aus dem "Solar" nachmal genauer angeschaut. Wie es aussieht, gibt es noch bei dem Output der Daten gewisse Diskrepanzen, wie zum Beispiel bei Energy.Consumption.Total.Day und Energy.Production.Total.Day

Unter System werden die Daten des Vortags angezeigt und unter Inverter die aktuellen Live Daten. Ich habe meine Konfiguration auf Inverter umgestellt und damit scheint es nun die richtigen Daten auszuspucken.

Hat eigentlich einer von euch die Enpal PIN für die Inverter Config? Leider hat sich bei mir die Sommerzeit noch nicht umgestellt :(

skehrer commented 2 months ago

Interessant, dass das bei Dir voneinander abweicht. Ich habe gerade eben mal geschaut und zumindest stand heute abend 22 Uhr sind die Werte unter System und Inverter bei mir identisch. Ich schaue mir das morgen früh nochmal an. Mal schauen ob es da bei mir dann trotzdem identische Werte sind oder ob das einfach erst im Lauf des Tages nachgezogen wird.

Bezüglich der Uhrzeit: nein, ich habe keine PIN für die Inverter Config. Welche Uhrzeit meinst Du, die bei Dir nicht korrekt ist? Bei mir ist die Uhrzeit der Werte im Menüpunkt "Messages" ebenfalls um zwei Stunden daneben. Allerdings gehe ich davon aus, dass diese Uhrzeit in UTC ausgeliefert wird. Das würde passen da es vor der Zeitumstellung bei mir eine Stunde war. Die Zeitstempel in der InfluxDB sind bei mir korrekt in CEST. Das scheint mir also (auf meinem System) soweit alles zu passen.

Kleine Randbemerkung noch: ich habe gerade gesehen dass ich seit heute eine neue Version der Solar habe, nämlich Rel 8.35.1 vom 10.04.2024. Das Schema der InfluxDB ist aber unverändert!

SvenAbels commented 2 months ago

Hat eigentlich einer von euch die Enpal PIN für die Inverter Config? Leider hat sich bei mir die Sommerzeit noch nicht umgestellt :(

Das habe ich hier auch und das Zeitproblem führte bei mit dazu, dass die Werte von Energy.Production.Total.Day z.b. verspätet auf 0 gesetzt wurden und daurch bei HomeAssistant das Energiedashboard nicht mehr stimmte. Ich habe das dadurch gelöst, dass ich einen Verbrauchszähler erstellt habe, der die Werte immer aufsummiert und dass ich den dann als Quelle im Energiedashboard genommen habe...

TheFlea80 commented 2 months ago

Bezüglich der Uhrzeit: nein, ich habe keine PIN für die Inverter Config. Welche Uhrzeit meinst Du, die bei Dir nicht korrekt ist? Bei mir ist die Uhrzeit der Werte im Menüpunkt "Messages" ebenfalls um zwei Stunden daneben. Allerdings gehe ich davon aus, dass diese Uhrzeit in UTC ausgeliefert wird. Das würde passen da es vor der Zeitumstellung bei mir eine Stunde war. Die Zeitstempel in der InfluxDB sind bei mir korrekt in CEST. Das scheint mir also (auf meinem System) soweit alles zu passen.

Bei mir passt die Uhrzeit auf dem Inverter selbst nicht. Die Daten in der Datenbank sind mit dem korrekten UTC-Zeitstempel versehen. Es ist mehr ein "Es triggert mich" als ein technisches Problem.

Kleine Randbemerkung noch: ich habe gerade gesehen dass ich seit heute eine neue Version der Solar habe, nämlich Rel 8.35.1 vom 10.04.2024. Das Schema der InfluxDB ist aber unverändert!

Ja, das kann ich bestätigen.

Was mir auch noch aufgefallen ist: Der Wert "Energy.External.Total.In.Day" (aktuell 6,6kWh) und "Energy.Grid.Import.Day" (aktuell 1,1kWh) sind beide unterschiedlich bei mir. Und beide sind falsch! In der ENPAL App allerdings wird der richtige Wert angezeigt (aktuell 3,4kWh). Ich verstehe zum verrecken nicht, wo die App sich den Wert herzieht. Soweit ich es rekonstruieren kann, ist der Wert Energy.External.Total.In.Day der gestrige Verbrauch. Bei den anderen Werte, stehe ich auf dem Schlauch...

SvenAbels commented 2 months ago

Was mir auch noch aufgefallen ist: Der Wert "Energy.External.Total.In.Day" (aktuell 6,6kWh) und "Energy.Grid.Import.Day" (aktuell 1,1kWh) sind beide unterschiedlich bei mir. Und beide sind falsch! In der ENPAL App allerdings wird der richtige Wert angezeigt (aktuell 3,4kWh). Ich verstehe zum verrecken nicht, wo die App sich den Wert herzieht.

Schau mal, ob Energy.Grid.Import.Day + Energy.Consumption.Total.Day in Summe bei dir die 3,4 kWh ergeben. Bei mir ist das so, dass beide Werte zusammen dem Netzimport entsprechen

TheFlea80 commented 2 months ago

Schau mal, ob Energy.Grid.Import.Day + Energy.Consumption.Total.Day in Summe bei dir die 3,4 kWh ergeben. Bei mir ist das so, dass beide Werte zusammen dem Netzimport entsprechen

Leider nein. Der Energy.Consumption.Total.Day Wert, ist bei mir der Gesamtverbrauch des Hauses. Ich müsste also, um mir den Wert zu errechnen folgendes tun: Gesamtverbrauch -Batterieladen -Batterieentladen -solarerzeugungTag -netzeinspeisung rechnen. Das setzt aber voraus, das ich den Akku nicht über das Netz geladen habe, wenn der Strom günstig ist. Alternativ baue ich mir einen ESP der mir den Verbrauch am Zähler direkt ermittelt. Das System von ENPAL scheint noch nicht richtig ausgereift zu sein...

GetUpdate commented 2 months ago

Denke vom Zähler, die Werte bekommt ENPAL direkt über die Antenne am Zähler, Der Zähler gibt diese m.E. nach nicht an die Hausinterne influx DB weiter.

GetUpdate commented 2 months ago

Hallo Zusammen,

habe dieses issue gestartet um die Fox Ess Harware ebenfalls auslesen zu können. Leider geht das mit der standard integration nach wie vor nicht. Nutze daher noch immer eine angepasste sensor.py. Ist es möglich diese zu integrieren oder braucht man für Fox Ess eine eigene Integration?

Gruß und Dank im Voraus M

stedel commented 2 months ago

hey ich glaube wir können, da es um enpal geht, auch deutsch schreiben ;D ich habe gerade eben mal ein pre release rein gestellt... habe ihr Zugang zu influxdb?? könnt ihr mir die genauen fields nennen. dann kann ich nochmal ein vernünftiges update machen.

Hi Gicko, Ich habe seit knapp 2 Wochen ebenfalls ein System von Enpal mit Inverter und 20 kWh Speicher vonFox ESS. Heute habe ich auch den "viewer"-Zugang von Enpal erhalten und mir Deine Integration gleich mal installiert und angeschaut. Vielen Dank für die Arbeit die Du da investiert hast. Bei mir funktionieren ebenfalls momentan einige der Sensoren nicht. Ich sehe mit der aktuellen Version der Integration folgendes:

  • Enpal Energy Consumption
  • Enpal Energy External In Day
  • Enpal Energy External Out Day
  • Enpal Power External Total
  • Enpal Power House Total
  • Enpal Production Day
  • Enpal Solar Production Power

Was mir nicht angezeigt wird sind sämtliche Batterie bezogenen Werte sowie die Enpal Voltage Phase A-C, Enpal Power Phase A-C, Enpal Ampere Phase A-C und die Werte zur Wallbox (habe ich keine). Auf die Schnelle musste ich feststellen, dass bei mir die relevanten measurements teilweise anders heißen als in Deiner Integration. Ich habe zum Beispiel kein measurement namens "battery". Bei mir stehen die entsprechenden Felder zum Großteil im measurement "inverter" und (teilweise gedoppelt und mit anderem Feldbezeichner) im measurement "system". Ein schneller Test bei dem ich das measurement für die Batteriegruppe durch "inverter" ersetzt habe hat dafür gesorgt, dass ich jetzt die entsprechenden Batteriesensoren angezeigt bekomme. Ich werde mir das in den nächsten Tagen mal genauer anschauen und Dir dann meinen Fix zukommen lassen. Ich kann Dir auch gerne das genaue Schema der influxDB die auf meinem System läuft (Solar Rel 8.35) zukommen lassen wenn Du mir sagst, auf welchem Weg ich das am besten machen soll. Als weitere kurze Information habe ich vom Enpal Service-Team noch folgende Aussage bekommen: "Wir arbeiten in diesem Jahr an einer größeren Umstrukturierung unserer Lösung die dann auch auf Ihrem System ausgerollt wird. Dies wird dann auf jeden Fall auch die Influx DB betreffen. Sie müssten dann noch einmal Anpassungen an Ihrem System vornehmen." Nur als kleine Warnung vorab, dass zumindest bei den neuen Enpal-Boxen wohl in absehbarer Zukunft noch das eine oder andere an Anpassungen fällig werden wird. Gruß, Stephan

Hallo, Sorry für die späte Rückmeldung, war 2 Wochen am Roten Meer und leider ist dort kein VPN möglich... Sehe mit der V02.2 auch wie @skehrer nur die Enitäten wie oben beschrieben.

Mir fehlt mit V0.2.2 noch immer die Werte wie ich oben angegeben habe (Battery und Phases). Mit meiner "abgeleiteten" Sensor.py klappt das jedoch auch mit der V02.2. wieder.

@skehrer Du kannst ja mal versuchen ob das dann bei Dir auch klappt. Die zu editierende Datei findest Du unter /homeassistant/custom_components/enpal/sensor.py -> Umbenennen und als Kopie behalten -> Inhalt von sensor.py mit werten unten überschreiben. -> HA neu starten...

Gruß Michael

Sensor.py:

"""Platform for sensor integration.""" from future import annotations

import asyncio import uuid from datetime import timedelta, datetime from homeassistant.components.sensor import (SensorEntity) from homeassistant.core import HomeAssistant from homeassistant import config_entries from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_registry import async_get, async_entries_for_config_entry from custom_components.enpal.const import DOMAIN import aiohttp import logging from influxdb_client import InfluxDBClient

_LOGGER = logging.getLogger(name) SCAN_INTERVAL = timedelta(seconds=20)

VERSION= '0.1.0'

def get_tables(ip: str, port: int, token: str): client = InfluxDBClient(url=f'http://{ip}:{port}', token=token, org='enpal') query_api = client.query_api()

query = 'from(bucket: "solar") \
  |> range(start: -5m) \
  |> last()'

tables = query_api.query(query)
return tables

async def async_setup_entry( hass: HomeAssistant, config_entry: config_entries.ConfigEntry, async_add_entities, ): # Get the config entry for the integration config = hass.data[DOMAIN][config_entry.entry_id] if config_entry.options: config.update(config_entry.options) to_add = [] if not 'enpal_host_ip' in config: _LOGGER.error("No enpal_host_ip in config entry") return if not 'enpal_host_port' in config: _LOGGER.error("No enpal_host_port in config entry") return if not 'enpal_token' in config: _LOGGER.error("No enpal_token in config entry") return

global_config = hass.data[DOMAIN]

tables = await hass.async_add_executor_job(get_tables, config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'])

for table in tables:
    field = table.records[0].values['_field']
    measurement = table.records[0].values['_measurement']

    if measurement == "inverter" and field == "Power.DC.Total":
        to_add.append(EnpalSensor(field, measurement, 'mdi:solar-power', 'Enpal Solar Production Power', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    if measurement == "inverter" and field == "Power.House.Total":
        to_add.append(EnpalSensor(field, measurement, 'mdi:home-lightning-bolt', 'Enpal Power House Total', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    if measurement == "system" and field == "Power.External.Total":
        to_add.append(EnpalSensor(field, measurement, 'mdi:home-lightning-bolt', 'Enpal Power External Total', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    # Consum Total per Day
    if measurement == "system" and field == "Energy.Consumption.Total.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:home-lightning-bolt', 'Enpal Energy Consumption', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

    # to the Grid and from the Grid
    if measurement == "system" and field == "Energy.External.Total.Out.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:transmission-tower-export', 'Enpal Energy External Out Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
    if measurement == "system" and field == "Energy.External.Total.In.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:transmission-tower-import', 'Enpal Energy External In Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

    # Solar Energy.Production.Total.Day
    if measurement == "system" and field == "Energy.Production.Total.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:solar-power-variant', 'Enpal Production Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

    #Battery    
    if field == "Power.Battery.Charge.Discharge":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-charging', 'Enpal Battery Power', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    if field == "Energy.Battery.Charge.Level":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery', 'Enpal Battery Percent', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'battery', '%'))
    if field == "Energy.Battery.Charge.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-up', 'Enpal Battery Charge Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
    if field == "Energy.Battery.Discharge.Day":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-down', 'Enpal Battery Discharge Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
    if field == "Energy.Battery.Charge.Load":
        to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-up', 'Enpal Battery Charge Total', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

    #Power Sensor -> Inverter
    if measurement == "inverter" and field == "Voltage.String.1":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage Phase A', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V'))
    if measurement == "inverter" and field == "Current.String.1":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere Phase A', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A'))
    if measurement == "inverter" and field == "Power.DC.String.1":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power Phase A', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
    if measurement == "inverter" and field == "Voltage.String.2":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage Phase B', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V'))
    if measurement == "inverter" and field == "Current.String.2":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere Phase B', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A'))
    if measurement == "inverter" and field == "Power.DC.String.2":
        to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power Phase B', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))        

entity_registry = async_get(hass)
entries = async_entries_for_config_entry(
    entity_registry, config_entry.entry_id
)
for entry in entries:
    entity_registry.async_remove(entry.entity_id)

async_add_entities(to_add, update_before_add=True)

class EnpalSensor(SensorEntity):

def __init__(self, field: str, measurement: str, icon:str, name: str, ip: str, port: int, token: str, device_class: str, unit: str):
    self.field = field
    self.measurement = measurement
    self.ip = ip
    self.port = port
    self.token = token
    self.enpal_device_class = device_class
    self.unit = unit
    self._attr_icon = icon
    self._attr_name = name
    self._attr_unique_id = f'enpal_{measurement}_{field}'
    self._attr_extra_state_attributes = {}

async def async_update(self) -> None:

    # Get the IP address from the API
    try:
        client = InfluxDBClient(url=f'http://{self.ip}:{self.port}', token=self.token, org="enpal")
        query_api = client.query_api()

        query = f'from(bucket: "solar") \
          |> range(start: -5m) \
          |> filter(fn: (r) => r["_measurement"] == "{self.measurement}") \
          |> filter(fn: (r) => r["_field"] == "{self.field}") \
          |> last()'

        tables = await self.hass.async_add_executor_job(query_api.query, query)

        value = 0
        if tables:
            value = tables[0].records[0].values['_value']

        self._attr_native_value = round(float(value), 2)
        self._attr_device_class = self.enpal_device_class
        self._attr_native_unit_of_measurement = self.unit
        self._attr_state_class = 'measurement'
        self._attr_extra_state_attributes['last_check'] = datetime.now()
        self._attr_extra_state_attributes['field'] = self.field
        self._attr_extra_state_attributes['measurement'] = self.measurement

        #if self.field == 'Energy.Consumption.Total.Day' or 'Energy.Storage.Total.Out.Day' or 'Energy.Storage.Total.In.Day' or 'Energy.Production.Total.Day' or 'Energy.External.Total.Out.Day' or 'Energy.External.Total.In.Day':
        if self._attr_native_unit_of_measurement == "kWh":
            self._attr_extra_state_attributes['last_reset'] = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
            self._attr_state_class = 'total'
        if self._attr_native_unit_of_measurement == "Wh":
            self._attr_extra_state_attributes['last_reset'] = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
            self._attr_state_class = 'total'

        if self.field == 'Percent.Storage.Level':
            if self._attr_native_value >= 10:
                self._attr_icon = "mdi:battery-outline"
            if self._attr_native_value <= 19 and self._attr_native_value >= 10:
                self._attr_icon = "mdi:battery-10"
            if self._attr_native_value <= 29 and self._attr_native_value >= 20:
                self._attr_icon = "mdi:battery-20"
            if self._attr_native_value <= 39 and self._attr_native_value >= 30:
                self._attr_icon = "mdi:battery-30"
            if self._attr_native_value <= 49 and self._attr_native_value >= 40:
                self._attr_icon = "mdi:battery-40"
            if self._attr_native_value <= 59 and self._attr_native_value >= 50:
                self._attr_icon = "mdi:battery-50"    
            if self._attr_native_value <= 69 and self._attr_native_value >= 60:
                self._attr_icon = "mdi:battery-60"
            if self._attr_native_value <= 79 and self._attr_native_value >= 70:
                self._attr_icon = "mdi:battery-70"
            if self._attr_native_value <= 89 and self._attr_native_value >= 80:
                self._attr_icon = "mdi:battery-80"
            if self._attr_native_value <= 99 and self._attr_native_value >= 90:
                self._attr_icon = "mdi:battery-90"        
            if self._attr_native_value == 100:
                self._attr_icon = "mdi:battery"

    except Exception as e:
        _LOGGER.error(f'{e}')
        self._state = 'Error'
        self._attr_native_value = None
        self._attr_extra_state_attributes['last_check'] = datetime.now()

Führt dann zu 18 Entitäten: image

Hi zusammen, ich habe vor 2 Wochen auf das von dir aktualisierte Skript umgestellt und ein paar Tage später dann noch etwas weiter entwickelt und jetzt mal eine Woche beobachtet. Hatte es einen Grund, die state_class wieder zurück auf total zu ändern? Das wurde ja ursprünglich mit dem letzten Release v0.2.2 gemacht und habe ich in meiner Anpassung auch wieder vorgenommen. Damit scheint es bei mir mit den Daten vom Stromzähler und Solar Rel. 8.36.1 (24.04.2024) übereinzustimmen. Abgesehen habe ich bei mir erstmal die Wallbox Daten entfernt da wir zurzeit noch keine haben.

"""Platform for sensor integration."""
from __future__ import annotations

import asyncio
import uuid
from datetime import timedelta, datetime
from homeassistant.components.sensor import (SensorEntity)
from homeassistant.core import HomeAssistant
from homeassistant import config_entries
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_registry import async_get, async_entries_for_config_entry
from custom_components.enpal.const import DOMAIN
import aiohttp
import logging
from influxdb_client import InfluxDBClient

_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=20)

VERSION= '0.1.0'

def get_tables(ip: str, port: int, token: str):
    client = InfluxDBClient(url=f'http://{ip}:{port}', token=token, org='enpal')
    query_api = client.query_api()

    query = 'from(bucket: "solar") \
      |> range(start: -5m) \
      |> last()'

    tables = query_api.query(query)
    return tables

async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: config_entries.ConfigEntry,
    async_add_entities,
):
    # Get the config entry for the integration
    config = hass.data[DOMAIN][config_entry.entry_id]
    if config_entry.options:
        config.update(config_entry.options)
    to_add = []
    if not 'enpal_host_ip' in config:
        _LOGGER.error("No enpal_host_ip in config entry")
        return
    if not 'enpal_host_port' in config:
        _LOGGER.error("No enpal_host_port in config entry")
        return
    if not 'enpal_token' in config:
        _LOGGER.error("No enpal_token in config entry")
        return

    global_config = hass.data[DOMAIN]

    tables = await hass.async_add_executor_job(get_tables, config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'])

    for table in tables:
        field = table.records[0].values['_field']
        measurement = table.records[0].values['_measurement']

        if measurement == "inverter" and field == "Power.DC.Total":
            to_add.append(EnpalSensor(field, measurement, 'mdi:solar-power', 'Enpal Solar Production Power', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
        if measurement == "inverter" and field == "Power.House.Total":
            to_add.append(EnpalSensor(field, measurement, 'mdi:home-lightning-bolt', 'Enpal Power House Total', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
        if measurement == "system" and field == "Power.External.Total":
            to_add.append(EnpalSensor(field, measurement, 'mdi:home-lightning-bolt', 'Enpal Power External Total', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
        # Consum Total per Day
        if measurement == "system" and field == "Energy.Consumption.Total.Day":
            to_add.append(EnpalSensor(field, measurement, 'mdi:home-lightning-bolt', 'Enpal Energy Consumption', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

        # to the Grid and from the Grid
        if measurement == "system" and field == "Energy.External.Total.Out.Day":
            to_add.append(EnpalSensor(field, measurement, 'mdi:transmission-tower-export', 'Enpal Energy External Out Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
        if measurement == "system" and field == "Energy.External.Total.In.Day":
            to_add.append(EnpalSensor(field, measurement, 'mdi:transmission-tower-import', 'Enpal Energy External In Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

        # Solar Energy.Production.Total.Day
        if measurement == "system" and field == "Energy.Production.Total.Day":
            to_add.append(EnpalSensor(field, measurement, 'mdi:solar-power-variant', 'Enpal Production Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

        #Battery
        if measurement == "inverter" and field == "Power.Battery.Charge.Discharge":
            to_add.append(EnpalSensor(field, measurement, 'mdi:battery-charging', 'Enpal Battery Power', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
        if measurement == "inverter" and field == "Energy.Battery.Charge.Level":
            to_add.append(EnpalSensor(field, measurement, 'mdi:battery', 'Enpal Battery Percent', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'battery', '%'))
        if measurement == "inverter" and field == "Energy.Battery.Charge.Day":
            to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-up', 'Enpal Battery Charge Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
        if measurement == "inverter" and field == "Energy.Battery.Discharge.Day":
            to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-down', 'Enpal Battery Discharge Day', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))
        if measurement == "inverter" and field == "Energy.Battery.Charge.Load":
            to_add.append(EnpalSensor(field, measurement, 'mdi:battery-arrow-up', 'Enpal Battery Charge Total', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'energy', 'kWh'))

        #Power Sensor -> Inverter
        if measurement == "inverter" and field == "Voltage.String.1":
            to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage String 1', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V'))
        if measurement == "inverter" and field == "Current.String.1":
            to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere String 1', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A'))
        if measurement == "inverter" and field == "Power.DC.String.1":
            to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power String 1', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))
        if measurement == "inverter" and field == "Voltage.String.2":
            to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Voltage String 2', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'voltage', 'V'))
        if measurement == "inverter" and field == "Current.String.2":
            to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Ampere String 2', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'current', 'A'))
        if measurement == "inverter" and field == "Power.DC.String.2":
            to_add.append(EnpalSensor(field, measurement, 'mdi:lightning-bolt', 'Enpal Power String 2', config['enpal_host_ip'], config['enpal_host_port'], config['enpal_token'], 'power', 'W'))        

    entity_registry = async_get(hass)
    entries = async_entries_for_config_entry(
        entity_registry, config_entry.entry_id
    )
    for entry in entries:
        entity_registry.async_remove(entry.entity_id)

    async_add_entities(to_add, update_before_add=True)

class EnpalSensor(SensorEntity):

    def __init__(self, field: str, measurement: str, icon:str, name: str, ip: str, port: int, token: str, device_class: str, unit: str):
        self.field = field
        self.measurement = measurement
        self.ip = ip
        self.port = port
        self.token = token
        self.enpal_device_class = device_class
        self.unit = unit
        self._attr_icon = icon
        self._attr_name = name
        self._attr_unique_id = f'enpal_{measurement}_{field}'
        self._attr_extra_state_attributes = {}

    async def async_update(self) -> None:

        # Get the IP address from the API
        try:
            client = InfluxDBClient(url=f'http://{self.ip}:{self.port}', token=self.token, org="enpal")
            query_api = client.query_api()

            query = f'from(bucket: "solar") \
              |> range(start: -5m) \
              |> filter(fn: (r) => r["_measurement"] == "{self.measurement}") \
              |> filter(fn: (r) => r["_field"] == "{self.field}") \
              |> last()'

            tables = await self.hass.async_add_executor_job(query_api.query, query)

            value = 0
            if tables:
                value = tables[0].records[0].values['_value']

            self._attr_native_value = round(float(value), 2)
            self._attr_device_class = self.enpal_device_class
            self._attr_native_unit_of_measurement   = self.unit
            self._attr_state_class = 'measurement'
            self._attr_extra_state_attributes['last_check'] = datetime.now()
            self._attr_extra_state_attributes['field'] = self.field
            self._attr_extra_state_attributes['measurement'] = self.measurement

            #if self.field == 'Energy.Consumption.Total.Day' or 'Energy.Storage.Total.Out.Day' or 'Energy.Storage.Total.In.Day' or 'Energy.Production.Total.Day' or 'Energy.External.Total.Out.Day' or 'Energy.External.Total.In.Day':
            if self._attr_native_unit_of_measurement == "kWh":
                self._attr_extra_state_attributes['last_reset'] = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
                self._attr_state_class = 'total_increasing'
            if self._attr_native_unit_of_measurement == "Wh":
                self._attr_extra_state_attributes['last_reset'] = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
                self._attr_state_class = 'total_increasing'

            if self.field == 'Percent.Storage.Level':
                if self._attr_native_value >= 10:
                    self._attr_icon = "mdi:battery-outline"
                if self._attr_native_value <= 19 and self._attr_native_value >= 10:
                    self._attr_icon = "mdi:battery-10"
                if self._attr_native_value <= 29 and self._attr_native_value >= 20:
                    self._attr_icon = "mdi:battery-20"
                if self._attr_native_value <= 39 and self._attr_native_value >= 30:
                    self._attr_icon = "mdi:battery-30"
                if self._attr_native_value <= 49 and self._attr_native_value >= 40:
                    self._attr_icon = "mdi:battery-40"
                if self._attr_native_value <= 59 and self._attr_native_value >= 50:
                    self._attr_icon = "mdi:battery-50"
                if self._attr_native_value <= 69 and self._attr_native_value >= 60:
                    self._attr_icon = "mdi:battery-60"
                if self._attr_native_value <= 79 and self._attr_native_value >= 70:
                    self._attr_icon = "mdi:battery-70"
                if self._attr_native_value <= 89 and self._attr_native_value >= 80:
                    self._attr_icon = "mdi:battery-80"
                if self._attr_native_value <= 99 and self._attr_native_value >= 90:
                    self._attr_icon = "mdi:battery-90"
                if self._attr_native_value == 100:
                    self._attr_icon = "mdi:battery"

        except Exception as e:
            _LOGGER.error(f'{e}')
            self._state = 'Error'
            self._attr_native_value = None
            self._attr_extra_state_attributes['last_check'] = datetime.now()
UniCizin commented 3 weeks ago

habs probiert und funtkioniert einwandfrei. Habe dann jetzt auch 17 Entitäten, Werte selbst habe ich noch nicht gecheckt

JuergenNe commented 1 week ago

Hallo, ich bekomme die Entität Batterie nicht. In der Enpal Konfiguration bzw. im Data-Collektor ist sie mit Storage benannt. Kennt jemand von euch das Problem. Leider bin ich noch ein ziemlicher Neuling und bekomme die Änderung alleine auch nicht hin.

JuergenNe commented 1 week ago

Hallo, ich habe meine Anlage mit den Komponenten von Fox vor 1 Woche bekommen. Es scheint, dass Enpal die Datenstruktur schon wieder ändert hat. Ich habe mit der Mail von Enpal mit den Zugangsdaten auch die Information bekommen, dass:

"Die InfluxDB ist auch Teil unserer zentralen SW-Lösung. Falls notwendig, kann es sein, dass wir hier kurzfristig Anpassungen durchführen müssen. Wir arbeiten in diesem Jahr an einer größeren Umstrukturierung unserer Lösung, die dann auch auf Ihrem System ausgerollt wird. Dies wird dann auf jeden Fall auch die InfluxDB betreffen. Sie müssten dann noch einmal Anpassungen an Ihrem System vornehmen."

Ich kann gerne versuchen die Datenbankstruktur auszulesen. Vielleicht kann mir dann jemand helfen, die Änderungen vorzunehmen.

GetUpdate commented 1 week ago

Enpal hat mir mitgeteilt das weitere Änderungen anstehen. Können wir uns also eh drauf einstellen.

Gruß

SvenAbels commented 1 week ago

Enpal hat mir mitgeteilt das weitere Änderungen anstehen. Können wir uns also eh drauf einstellen.

Sollten sie zu viel umbauen, könnten wir zur Not auch die Webseite der Enpal-Box parsen. Dafür habe ich mir ein kleines Skript geschrieben. Das ist zwar nicht so schön wie das Abfragen der InfluxDB, aber es erfüllt seinen Zweck...

Viele Grüße,

Sven

GetUpdate commented 6 days ago

Enpal hat mir mitgeteilt das weitere Änderungen anstehen. Können wir uns also eh drauf einstellen.

Sollten sie zu viel umbauen, könnten wir zur Not auch die Webseite der Enpal-Box parsen. Dafür habe ich mir ein kleines Skript geschrieben. Das ist zwar nicht so schön wie das Abfragen der InfluxDB, aber es erfüllt seinen Zweck...

Viele Grüße,

Sven

Cool :-) Gut zu Wissen. Hörte sich eher nach Änderungen der Metadaten / Formatierungen an, falls nicht komme ich gerne auf Dich zurück. Gruß und Dank

skehrer commented 6 days ago

Ich habe mir gerade mal das aktuelle DB Format (bei mir die 8.39.1 vom 21.06.2024) angeschaut. So viel hat sich zur Version 8.35.1 tatsächlich gar nicht getan. Im Measurement "inverter" sind ein paar Werte dazugekommen, aber die bereits bestehenden Werte wurden nicht verändert. Neu sind:

In den anderen Measurements gab es keine Veränderungen.

Meine für die 8.35.1 lokal angepasste Version der sensor.py liefert mir nach wie vor alle relevanten Werte. Ich habe meine aktuelle Version der sensor.py mal an diesen Post angehängt. @JuergenNe Du kannst ja mal testen ob Du mit der version auch die Werte für die Batterie siehst. Bei mir klappt es und ich habe auch die FoxESS Komponenten. Github hat mich hier leider keine .py Datei anhängen lassen, daher habe ich sie zu .txt umbenannt. Du musst die Datei also wieder von einer .txt. zu einer .py umbenennen.

Gruß, Stephan sensor.txt

JuergenNe commented 6 days ago

@skehrer Hallo, jetzt hab ich auch 22 Entitäten. Super. Vielen Dank.

Darf ich unverschämt ragen, ob du ein ansprechendes Dashboard schon konfiguriert hast, das ich verwenden könnte.

Vielen Dank

Gruß, Jürgen