tolwi / hassio-ecoflow-cloud

EcoFlow Cloud Integration for Home Assistant
369 stars 62 forks source link

Power Kits & Power Hub #131

Open ErikBussink opened 1 year ago

ErikBussink commented 1 year ago

Thanks to this amazing project, I have access to all my data for my Delta Pro & Extra Battery and the PowerStream.

I also have a Power Kit composed of a PowerHub and 3 LFP Batteries 5kWh. I've run the diagnostics against it and I will attempt to start the support for this Device and Batteries. I'm not a developer, just a tinkerer so bear with me on this.

paule96 commented 1 year ago

Hi @ErikBussink,

can I somehow help you. I have the same problem / situation. So if you need help with programming something or some data I can try to provide it.

lprhodes commented 8 months ago

I'll be adding this over the next 24h

lprhodes commented 8 months ago

The crux of it is working but I still have some work to do tomorrow to ensure all sensors and options are added.

lprhodes commented 8 months ago

I find it interesting that with the data we can see exactly how much power is going to the BMS and voltage conversions

foxthefox commented 5 months ago

The crux of it is working but I still have some work to do tomorrow to ensure all sensors and options are added.

can you post the JSON string for the latestQuotas or if not identical to the cyclic updates, them also the JSON of the cyclic response?

paule96 commented 4 months ago

@lprhodes I started now working too on the support for the power kit.

I used some of your code for a quick start. My work in progress can be viewed here:

https://github.com/paule96/hassio-ecoflow-cloud/tree/powerkit

foxthefox commented 3 months ago

@paule96 can you post a JSON containing latestQuotas and the cyclic telegrams from power kit?

paule96 commented 3 months ago

@foxthefox there seems to be an update of the API since this weekend. So before this weekend the json was structure like the following:

{
    "code": "0",
    "message": "Success",
    "data": {
        "bbcout": "{some sub json}",
        "lddc": "{some sub json}",
        "bp5000": "{some sub json}",
        "bmsTotal": "{some sub json}",
        "ldac": "{some sub json}",
        "onLineModuleSnList": "{some sub json}",
        "kitscc": "{some sub json}",
        "wireless": "{some sub json}",
        "bbcin": "{some sub json}",
        "ichigh": "{some sub json}",
        "iclow": "{some sub json}"
    },
    "eagleEyeTraceId": "xxxxx",
    "tid": ""
}

each subjson was a string usually with an JSON Object that contains the sub devices as Ids. So for example the content of bbcout could look like this:

{
   "SN-OF-SUBDEVICE": {
                "dsgEnergy": 78219,
                "ldOutCurr": 3625,
                "ldOutWatts": 95,
                "workMode": 2,
                "hs1Temp": 39,
                "dcOutSta": 1,
                "l1Curr": 1349,
                "batCurr": 1531,
                "warnCode": 0,
                "batWatts": 77,
                "eventCode": 0,
                "l2Curr": 1430,
                "cfgVolTag": 1,
                "pcbTemp": 0,
                "errCode": 15000,
                "hs2Temp": 0,
                "standbyTime": 0,
                "batVol": 50481,
                "ldOutVol": 26408,
                "dayEnergy": 12343
            }
}

Now the response looks like:

{
    "code": "0",
    "message": "Success",
    "data": {
        "bbcout": {
            "snofdevice": {
                "dsgEnergy": 78219,
                "ldOutCurr": 3625,
                "ldOutWatts": 95,
                "workMode": 2,
                "hs1Temp": 39,
                "dcOutSta": 1,
                "l1Curr": 1349,
                "batCurr": 1531,
                "warnCode": 0,
                "batWatts": 77,
                "eventCode": 0,
                "l2Curr": 1430,
                "cfgVolTag": 1,
                "pcbTemp": 0,
                "errCode": 15000,
                "hs2Temp": 0,
                "standbyTime": 0,
                "batVol": 50481,
                "ldOutVol": 26408,
                "dayEnergy": 12343
            }
        },
        "lddc": {
            "snofdevice": {
                "dcTemp1": 27,
                "dcTemp2": 24,
                "dcChRelay": 33,
                "dcChCur": [
                    3676,
                    0,
                    0,
                    0,
                    0,
                    141,
                    0,
                    0,
                    0,
                    0,
                    0,
                    381
                ],
                "dcChSta": 4065,
                "errorCodeAdd": [
                    40000,
                    40040,
                    40080,
                    40120,
                    40160,
                    40200,
                    40240,
                    40280,
                    40320,
                    40360,
                    40400,
                    40440,
                    40800
                ],
                "dcTotalWatts": 110,
                "dcSetChSta": 0,
                "dcInVol": 26664,
                "dcChWatt": [
                    97,
                    0,
                    0,
                    0,
                    0,
                    3,
                    0,
                    0,
                    0,
                    0,
                    0,
                    10
                ]
            }
        },
        "bp5000": {
            "snofdevice": {
                "chgDsgMosState": 3,
                "soc": 5,
                "ptcHeatingEvent": 6,
                "minCellVol": 3178,
                "upsFlag": 0,
                "warnCode": 0,
                "lcdOffConfirmS": 300,
                "ptcChgErrCnt": 0,
                "oilStopUpline": 80,
                "remainCap": 4975,
                "chgState": 1,
                "acDcLsplShutdMin": 0,
                "canId": 1,
                "maxCellTemp": 22,
                "lcdStandbyMin": 300,
                "errCode": 50000,
                "minPtcTemp": 21,
                "totalAmp": 65522,
                "warningEvent": 0,
                "designCap": 100000,
                "kitNum": 1,
                "oilCloseSoc": 80,
                "totalChgDsgState": 1,
                "totalFullCap": 94,
                "amp": -1533,
                "fullCap": 94831,
                "totalOutWatts": -75,
                "ptcRemainTime": 0,
                "totalInWatts": 0,
                "vol": 50921,
                "openBmsIdex": 1,
                "ptcHeatingFlag": 0,
                "remindDsgPtcFlag": 0,
                "minCellTemp": 21,
                "inWatts": 0,
                "proChgDsgMosState": 3,
                "temp": 21,
                "bmsChgUpline": 100,
                "maxMosTemp": 21,
                "doubleOilErrorFlag": 0,
                "remainTime": 181,
                "ptcMosErr": 0,
                "minMosTemp": 21,
                "ptcAllowFlag": 0,
                "maxPtcTemp": 22,
                "eventCode": 50500,
                "maxCellVol": 3191,
                "totalSoc": 5,
                "dsgSetSoc": 0,
                "ptcTouchFlag": 0,
                "balanceFlag": 0,
                "totalRemainTime": 185,
                "bmsFault": 0,
                "oilStartDownline": 20,
                "bmsType": 0,
                "chgSetSoc": 100,
                "oilOpenSoc": 20,
                "outWatts": -78,
                "bmsDsgDownline": 0
            }
        },
        "bmsTotal": {
            "totalChgDsgState": 1,
            "bmsChgUpline": 100,
            "totalFullCap": 94,
            "totalOutWatts": -75,
            "doubleOilErrorFlag": 0,
            "acDcLsplShutdMin": 0,
            "totalInWatts": 0,
            "totalSoc": 5,
            "lcdOffConfirmS": 300,
            "remindDsgPtcFlag": 0,
            "totalAmp": 65522,
            "totalRemainTime": 185,
            "warningEvent": 0,
            "oilStartDownline": 20,
            "oilStopUpline": 80,
            "bmsDsgDownline": 0
        },
        "ldac": {
            "snofdevice": {
                "acTemp1": 31,
                "acChWatt": [
                    0,
                    0,
                    0,
                    0,
                    0,
                    0
                ],
                "errorCodeAdd": [
                    30000,
                    30040,
                    30080,
                    30120,
                    30160,
                    30200,
                    30800
                ],
                "acChCur": [
                    538,
                    0,
                    0,
                    0,
                    0,
                    0
                ],
                "acChSta": 1,
                "acTemp2": 33,
                "acInVol": 233058,
                "acTotalWatts": 6,
                "acSetChSta": 0
            }
        },
        "onLineModuleSnList": [
            {
                "customData": "0",
                "loaderVersion": "1.2.2.2",
                "moduleAddr": "2",
                "moduleDAddr": "1",
                "moduleDetail": "5",
                "moduleSn": "snofdevice",
                "moduleType": "60",
                "moduleVersion": "5.3.5.153"
            },
            {
                "customData": "0",
                "loaderVersion": "1.1.2.2",
                "moduleAddr": "4",
                "moduleDAddr": "1",
                "moduleDetail": "6",
                "moduleSn": "snofdevice",
                "moduleType": "60",
                "moduleVersion": "5.3.4.212"
            },
            {
                "customData": "0",
                "loaderVersion": "4.0.2.2",
                "moduleAddr": "84",
                "moduleDAddr": "1",
                "moduleDetail": "8",
                "moduleSn": "snofdevice",
                "moduleType": "60",
                "moduleVersion": "0.3.0.51"
            },
            {
                "customData": "0",
                "loaderVersion": "2.0.2.1",
                "moduleAddr": "81",
                "moduleDAddr": "1",
                "moduleDetail": "3",
                "moduleSn": "snofdevice",
                "moduleType": "60",
                "moduleVersion": "0.3.1.79"
            },
            {
                "customData": "1",
                "loaderVersion": "1.0.1.15",
                "moduleAddr": "3",
                "moduleDAddr": "1",
                "moduleDetail": "1",
                "moduleSn": "snofdevice",
                "moduleType": "60",
                "moduleVersion": "1.1.2.68"
            },
            {
                "customData": "0",
                "loaderVersion": "3.0.2.2",
                "moduleAddr": "83",
                "moduleDAddr": "1",
                "moduleDetail": "7",
                "moduleSn": "snofdevice",
                "moduleType": "60",
                "moduleVersion": "0.3.0.52"
            },
            {
                "customData": "0",
                "loaderVersion": "2.0.2.1",
                "moduleAddr": "80",
                "moduleDAddr": "1",
                "moduleDetail": "2",
                "moduleSn": "snofdevice",
                "moduleType": "60",
                "moduleVersion": "0.3.0.199"
            },
            {
                "customData": "0",
                "loaderVersion": "1.0.2.2",
                "moduleAddr": "5",
                "moduleDAddr": "1",
                "moduleDetail": "4",
                "moduleSn": "snofdevice",
                "moduleType": "60",
                "moduleVersion": "0.3.0.163"
            }
        ],
        "kitscc": {
            "snofdevice": {
                "pv2ErrCode": 0,
                "pv1InWatts": 0,
                "pv2InWatts": 45,
                "pv1ErrCode": 0,
                "chgEnergy": 0,
                "l1Curr": 0,
                "alt2CableUnit": 0,
                "batCurr": 899,
                "batWatts": 45604,
                "pv1InCurr": 0,
                "pv2InVol": 62076,
                "alt1VoltLmtEn": 1,
                "pv2WorkMode": 1,
                "alt1CableLen": 2000,
                "hs2Temp": 37,
                "mppt2SwSta": 1,
                "alt2CableLen": 600,
                "alt2VoltLmtEn": 1,
                "pv1_hot_out": 1,
                "pv2_hot_out": 0,
                "dayEnergy": 0,
                "mppt1SwSta": 0,
                "dsgEnergy": 0,
                "alt1VoltLmt": 303,
                "alt2VoltLmt": 123,
                "hs1Temp": 35,
                "pv2InputFlag": 1,
                "pv1InputFlag": 0,
                "pv2InCurr": 736,
                "pv1WorkMode": 1,
                "l2Curr": 826,
                "eventCode2": 0,
                "pcbTemp": 0,
                "eventCode1": 0,
                "batVol": 50869,
                "pv1InVol": 0,
                "alt1CableUnit": 0,
                "warnCode2": 0,
                "warnCode1": 0
            }
        },
        "wireless": {
            "snofdevice": {
                "scenes": 4
            }
        },
        "bbcin": {
            "snofdevice": {
                "chgPause": 1,
                "workMode": 1,
                "l1Curr": 28,
                "bpOnlinePos": 1,
                "altCableUnit": 0,
                "batCurr": 78,
                "warnCode": 0,
                "batWatts": 3,
                "altCableLen": 900,
                "altVoltLmt": 123,
                "altVoltLmtEn": 1,
                "hs2Temp": 31,
                "dcInState": 0,
                "workMode2": 0,
                "dayEnergy": 1234,
                "dsgEnergy": 789,
                "inHwTpe": 0,
                "allowDsgOn": 1,
                "hs1Temp": 29,
                "dcInWatts": 0,
                "dcInCurr": 0,
                "chgType": 0,
                "shakeCtrlDisable": 1,
                "eventCode": 0,
                "l2Curr": 26,
                "chargeMode": 0,
                "pcbTemp": 0,
                "errCode": 10000,
                "batVol": 50013,
                "dcInVol": 0,
                "chgMaxCurr": 60000,
                "isCarMoving": 1
            }
        },
        "ichigh": {
            "snofdevice": {
                "outAmp2": 0,
                "invType": 0,
                "inFreq": 0,
                "invSwSta": 1,
                "appCfg": {
                    "powerOn": 1,
                    "wakeup": 0,
                    "standbyTime": 0,
                    "acRlyCtrlDisable": 1,
                    "acFreqSet": 50,
                    "acMaxCurrSer": 2,
                    "acVolSet": 230,
                    "acChgDisable": 1,
                    "passByMaxCurr": 14
                },
                "outVa": 155,
                "inCurr": 0,
                "inVol": 0,
                "outVol": 229982,
                "acTemp": 49,
                "standbyTime": 30,
                "outFreq": 50,
                "cfgOutFreq": 50,
                "outputWhInDay": 0,
                "inputWhInDay": 0,
                "outCurr": 786,
                "ch2Watt": 21,
                "outWatts": 40,
                "inWatts": 0
            }
        },
        "iclow": {
            "snofdevice": {
                "bmsChgCurr": 291,
                "realSoc": 5,
                "warn_code": 0,
                "protectState": 0,
                "chgType": 1,
                "batCurr": 292,
                "dcTemp": 47,
                "chgDsgState": 1,
                "extKitType": 0,
                "busVol": 388442,
                "lsplFlag": 0,
                "maxChgCurr": 0,
                "errCode": 0,
                "event_code": 0,
                "chrgFlag": 1,
                "batVol": 0,
                "fanLevel": 2,
                "chgInType": 1,
                "chgBatVol": 50910
            }
        }
    },
    "eagleEyeTraceId": "xxxx",
    "tid": ""
}

Note: Everywhere where snofdevice is written in the json, the serial number of the subdevice is in the original JSON there. (for example from the battery)

foxthefox commented 3 months ago

thank you very much!

again a different approach in data structure, but luckily still JSON

Maybe you the answers to some questions:

paule96 commented 3 months ago

yes I'm really happy with this change. Even if it means my work on the weekend is not anymore needed 🤷‍♀️ But it was so annoying for a year that each subgroup of devices were strings that contained json....

I didn't look to closely at the response of the MQTT commands. So the response above is from the REST API. But if you want to take a closer look go to this branch and run the debug.init.py.

Then you see what you receive from MQTT at some point. (Sorry that I can't hint you to a file right now, I will maybe at next weekend)

But warning: The code may be broken right now, because I was expecting the string containing json. So maybe you must remove this string to json line here

tolwi commented 3 months ago

@paule96 take a look at this branch - it is abandoned, but you can catch the way on how to handle nested json

foxthefox commented 3 months ago

@paule96 looking to the complete JSON, the bmsTotal has no "snofdevice", thats not a problem, but it is different to the others. Looking to your powerkit.py I would assume that the incomming JSON has already be somehow tweaked to get rid of the intermediate "snofdevice", otherwise the values most likely are not catched with the expresssion. Also the commands seem to be unchanged from a powertstation ('pd' is not part of the powerkit), the commands somehow not so clear for me.

paule96 commented 3 months ago

@tolwi and @foxthefox thanks for the input. How can I produce more than one device per integration entry? image

Because in my understanding, the powerhub of the powerkit is more like a gateway that manages other devices, like the screen, like the smartdistribution panel, like the batteries, or even another hub (if you use it in combination with the smart panel)

paule96 commented 3 months ago

@foxthefox the json was unmodified, just the SNs where replaced. And yes it is weird that bmstotal has no sn. But also like understandable. Because I guess it's like a sum entity over all connected batteries. So it's directly connected to the powerhub

foxthefox commented 3 months ago

@tolwi and @foxthefox thanks for the input. How can I produce more than one device per integration entry?

Because in my understanding, the powerhub of the powerkit is more like a gateway that manages other devices, like the screen, like the smartdistribution panel, like the batteries, or even another hub (if you use it in combination with the smart panel)

For the powerkit it seems that there are different "subdevices" which might need to be treated as another devices. From my perspective there is still the same ID of the main device (may be the "gateway") so the "subdevices" can be treated in the same way as the different portions of a powerstation (bms,pd,ems,mppt,...). At powerstations they do not appear as "subdevices with own ID/moduleSn" but actually they are used in the same way. In any case additional batteries are special. At powerstations they appear as bms..1 or bms..2 and at powerkit it is a next serial# within bp2000/bp5000. So again it is compareable.

The question would be more how the different "channels" could be used to structure the data within the integration. Otherwise all numbers (indpendent from the channel/subdevice) appear in one set. This is more serious when all available data is read into the integration. As stated already somewhere else, I have still problems to understand the Homeassistant approach of managment of objects and the way how to setup a development environment, so I am not sure if intermediate structures like channel/subdevice is supported or not.

I am working on an ioBroker adapter for EF which in the meantime is also a HA gateway. It uses the MQTT discovery function and there is a hierarchical approach possible. See some screenshots:

entry with several devices:

Bildschirmfoto 2024-06-09 um 11 16 53

one device with channels/subdevices:

Bildschirmfoto 2024-06-09 um 11 18 22

the channel/subdevice:

Bildschirmfoto 2024-06-09 um 11 19 44

maybe it gives an idea for the integration. But I am not sure if it is supported at all, when using the native ways (instead of MQTT).

paule96 commented 3 months ago

hm okay. if we are not sure if it's supported maybe we should then just provide each devices as it's own root device. From the ecoflow mqtt documentation for the powerkit, it is possible to query each device by itself. For example, for the bbcin it is:

{ "sn": "M106ZAB4Z000001F", "params": { "quotas": [ "bbcin" ] } }

Then the question is, how todo it in the registration / config step. Currently my understanding of this integration is that in the following lines of code, you get the devices implmentation and register it as an homeassistant device:

from .devices.registry import devices
            device = devices[user_input[CONF_DEVICE_TYPE]]

            options = {OPTS_POWER_STEP: device.charging_power_step(), OPTS_REFRESH_PERIOD_SEC: DEFAULT_REFRESH_PERIOD_SEC}

            return self.async_create_entry(title=user_input[const.CONF_NAME], data=user_input, options=options)

But it would be nice if devices would be just a list. But then the problem is, how to add the SN of the subdevices and the category (like 'bbcin').

P.S.: @foxthefox I'm still struggling with how this integration works at all. If you have time today, it would be nice to have a hands-on call later. (Right now I'm in German train, but in around 2h to 3h I should be available, for example for a teams call)

foxthefox commented 3 months ago

Ich weiß leider nicht wie man einen privaten Chat über git machen kann, aber wenn es keine Umstände macht, dann können wir im https://forum.iobroker.net/ Infos austauschen, dort auch als foxthefox.

paule96 commented 2 months ago

to just give a little status update to all: I'm now able to ready all the data from a power kit with a single battery and a distribution panel connected to it.

The next steps are to implement multi battery support and most of the switches. Then I will be confident to create a pull request.

But right now you here you can see some of the data it is able to produce: image

diablo581 commented 2 weeks ago

Has there been any update on this? I would also like support for this if possible.

foxthefox commented 2 weeks ago

When working on the iobroker adapter which uses the unofficial API I had discovered that even there is an incomming JSON object the specific Objects like bbcout/bbcin... are wrapped additionally into a string. So you need to treat the message and it parts in a special way. I have solved that with version 1.04 of my adapter. Beside of that discovery there are also update telegrams which contain hourly energy values, which probably are not part of the official API update.

paule96 commented 2 weeks ago

@diablo581 I'm close to make a pull request to this repository. I fight a lot with the python stuff lately and most of the stuff works in theory :)

Need to cleanup some code.

If you want to see the current state of it you can take a look here.