klejejs / python-thermia-online-api

A Python API for Thermia heat pumps using https://online.thermia.se
GNU General Public License v3.0
29 stars 10 forks source link

ITec missing operational status #14

Closed oskari closed 1 year ago

oskari commented 1 year ago

Example how its shown on thermia online (classic), unfortunately I couldn't change the UI language.

image

Running example.py / debug lists:

Operational status
Operational status: None
Available operational statuses: None
Available operational statuses map: None
Auxiliary heater 3KW: None
Auxiliary heater 6KW: None
Auxiliary heater 9KW: None
Auxiliary heater 12KW: None
Auxiliary heater 15KW: None
Compressor status: None
Brine pump status: None
Radiator pump status: None
Cooling status: None
Hot water status: None
Heating status: None
Integral: None

I think the related entry in debug.txt is:


Group REG_GROUP_OPERATIONAL_STATUS:
[
    {
        "canBeShownInGraph": false,
        "canBeShownInHistory": true,
        "collapsible": false,
        "disabledByLink": false,
        "falseText": null,
        "graphRegisterIndex": 10031,
        "groupId": 4,
        "groupItemId": 386,
        "groupItemOrder": 70001,
        "groupName": "REG_GROUP_OPERATIONAL_STATUS",
        "groupOrder": 60010,
        "groupParentId": null,
        "isComputedRegister": false,
        "isInvisible": false,
        "isReadOnly": true,
        "maxValue": null,
        "minValue": null,
        "modifier": 4,
        "precision": null,
        "presentation": "Integer",
        "registerId": 10031,
        "registerIndex": 2120,
        "registerName": "REG_PID",
        "registerValue": -23,
        "step": 1,
        "stringRegisterValue": null,
        "timeStamp": "2022-12-30T13:28:59.303Z",
        "trueText": null,
        "unit": "NotSpecified",
        "valueNames": null
    },
    {
        "canBeShownInGraph": false,
        "canBeShownInHistory": false,
        "collapsible": false,
        "disabledByLink": false,
        "falseText": null,
        "graphRegisterIndex": 10002,
        "groupId": 4,
        "groupItemId": 400,
        "groupItemOrder": 70020,
        "groupName": "REG_GROUP_OPERATIONAL_STATUS",
        "groupOrder": 60010,
        "groupParentId": null,
        "isComputedRegister": true,
        "isInvisible": false,
        "isReadOnly": true,
        "maxValue": null,
        "minValue": null,
        "modifier": 0,
        "precision": null,
        "presentation": "StatusFlagsTextInd",
        "registerId": 10002,
        "registerIndex": 10002,
        "registerName": "COMP_STATUS_ITEC",
        "registerValue": 19,
        "step": 1,
        "stringRegisterValue": null,
        "timeStamp": "2022-12-30T14:39:30.9215449Z",
        "trueText": null,
        "unit": "0",
        "valueNames": [
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "COMP_VALUE_COMPR",
                "value": 1,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "COMP_VALUE_RADIATORPUMP",
                "value": 2,
                "visible": false
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "COMP_VALUE_HOT_WATER",
                "value": 8,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "COMP_VALUE_HEATING",
                "value": 16,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "COMP_VALUE_DEFROST",
                "value": 32,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "COMP_VALUE_POOL",
                "value": 64,
                "visible": false
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "COMP_VALUE_COOLING",
                "value": 128,
                "visible": true
            }
        ]
    }
]`
wouterse commented 1 year ago

@oskari, you observe that COMP_STATUS_ITEC is 19. This value is a binary number: 19 = 16 + 2 + 1, which means that:

1: COMP_VALUE_COMPR is on, i.e. the compressor is running. 2: COMP_VALUE_RADIATORPUMP is on, i.e. the radiator pump is running 16: COMP_VALUE_HEATING is on, i.e. the compressor is used for heating

So, this looks like normal behaviour.

oskari commented 1 year ago

But I don't think this library is currently reading it as so? I was expecting Compressor status to be something else than None? Am I missing something here or understood this wrong?

klejejs commented 1 year ago

@oskari Seems that your heat pump has a different compressor status register name than others, which is why the compressor status for you is None. The same with REG_PID, it was not present for others, so I have not implemented it for others.

Thanks for the heads up, I will add support for your heat pump with the next release.

oskari commented 1 year ago

@klejejs Great, thanks! Is there anything else I could provide? Do you want the full debug.txt for this unit? It's an 2017 Thermia iTec

klejejs commented 1 year ago

@oskari No, thanks! This is enough data for me unless there are any other values that you don't see and want to have in the API.

Gjorret commented 1 year ago

I believe this is the same for mine, which is an Atlas 18, even though im not sure if this is available. I dont believe i can see the data OP screenshoted, fx if the compressor is running in the dashboard.

Although i do see the Operational Status (Heating) which is why i guess its the same?

Using the genesis api:

Operational status
Operational status: None
Available operational statuses: None
Available operational statuses map: None
Auxiliary heater 3KW: None
Auxiliary heater 6KW: None
Auxiliary heater 9KW: None
Auxiliary heater 12KW: None
Auxiliary heater 15KW: None
Compressor status: None
Brine pump status: None
Radiator pump status: None
Cooling status: None
Hot water status: None
Heating status: None
Integral: None
Group REG_GROUP_OPERATIONAL_STATUS:
[
    {
        "canBeShownInGraph": false,
        "canBeShownInHistory": true,
        "collapsible": false,
        "disabledByLink": false,
        "falseText": null,
        "graphRegisterIndex": 33663,
        "groupId": 2,
        "groupItemId": 39,
        "groupItemOrder": 10000,
        "groupName": "REG_GROUP_OPERATIONAL_STATUS",
        "groupOrder": 10010,
        "groupParentId": null,
        "isComputedRegister": false,
        "isInvisible": false,
        "isReadOnly": true,
        "maxValue": null,
        "minValue": null,
        "modifier": 0,
        "precision": null,
        "presentation": "Enumeration",
        "registerId": 33663,
        "registerIndex": 151,
        "registerName": "REG_OPERATIONMODE",
        "registerValue": 3,
        "step": 1,
        "stringRegisterValue": null,
        "timeStamp": "2022-12-30T05:46:32.607Z",
        "trueText": null,
        "unit": "NotSpecified",
        "valueNames": [
            {
                "confirmation": null,
                "isReadonly": true,
                "name": "REG_VALUE_OPERATION_MODE_OFF",
                "value": 1,
                "visible": true
            },
            {
                "confirmation": "UI_OPERATION_MODE_AUX_HEATER_ONLY_WARNING",
                "isReadonly": false,
                "name": "REG_VALUE_OPERATION_MODE_SERVICE",
                "value": 2,
                "visible": false
            },
            {
                "confirmation": "UI_OPERATION_MODE_AUX_HEATER_ONLY_WARNING",
                "isReadonly": false,
                "name": "REG_VALUE_OPERATION_MODE_AUX_HEATER_ONLY",
                "value": 2,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_OPERATION_MODE_AUTO",
                "value": 3,
                "visible": true
            }
        ]
    },
    {
        "canBeShownInGraph": false,
        "canBeShownInHistory": true,
        "collapsible": false,
        "disabledByLink": false,
        "falseText": null,
        "graphRegisterIndex": 33668,
        "groupId": 2,
        "groupItemId": 14584,
        "groupItemOrder": 10002,
        "groupName": "REG_GROUP_OPERATIONAL_STATUS",
        "groupOrder": 10010,
        "groupParentId": null,
        "isComputedRegister": false,
        "isInvisible": false,
        "isReadOnly": true,
        "maxValue": null,
        "minValue": null,
        "modifier": 0,
        "precision": null,
        "presentation": "MultipleStatusFlags",
        "registerId": 33668,
        "registerIndex": 1987,
        "registerName": "REG_OPERATIONAL_STATUS_PRIORITY_BITMASK",
        "registerValue": 8,
        "step": 1,
        "stringRegisterValue": null,
        "timeStamp": "2022-12-30T07:56:32.388Z",
        "trueText": null,
        "unit": "NotSpecified",
        "valueNames": [
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_STATUS_MANUAL",
                "value": 1,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_STATUS_DEFROST",
                "value": 2,
                "visible": false
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_STATUS_HOTWATER",
                "value": 4,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_STATUS_HEAT",
                "value": 8,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_STATUS_COOL",
                "value": 16,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_STATUS_POOL",
                "value": 32,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_STATUS_LEGIONELLA",
                "value": 64,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_STATUS_PASSIVE_COOL",
                "value": 128,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_STATUS_STANDBY",
                "value": 512,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_STATUS_NO_DEMAND",
                "value": 1024,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_OPERATION_MODE_OFF",
                "value": 2048,
                "visible": true
            }
        ]
    }
]
klejejs commented 1 year ago

@Gjorret I assume you already see this data in available_operation_modes property, is that correct?

[
            {
                "confirmation": null,
                "isReadonly": true,
                "name": "REG_VALUE_OPERATION_MODE_OFF",
                "value": 1,
                "visible": true
            },
            {
                "confirmation": "UI_OPERATION_MODE_AUX_HEATER_ONLY_WARNING",
                "isReadonly": false,
                "name": "REG_VALUE_OPERATION_MODE_SERVICE",
                "value": 2,
                "visible": false
            },
            {
                "confirmation": "UI_OPERATION_MODE_AUX_HEATER_ONLY_WARNING",
                "isReadonly": false,
                "name": "REG_VALUE_OPERATION_MODE_AUX_HEATER_ONLY",
                "value": 2,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_OPERATION_MODE_AUTO",
                "value": 3,
                "visible": true
            }
        ]
klejejs commented 1 year ago

The latest 3.7 version should include operational modes for your heat pumps and PID property for the ITec heat pump.

oskari commented 1 year ago

Running example.py results in:

Operational status
Operational status: None
Traceback (most recent call last):
  File "xxx/python-thermia-online-api/example.py", line 55, in <module>
    "Available operational statuses: " + str(heat_pump.available_operational_statuses)
  File "xxx/python-thermia-online-api/ThermiaOnlineAPI/model/HeatPump.py", line 572, in available_operational_statuses
    data = self.__get_all_operational_statuses_from_operational_status()
  File "xxx/python-thermia-online-api/ThermiaOnlineAPI/model/HeatPump.py", line 396, in __get_all_operational_statuses_from_operational_status
    operation_modes_list = list(operation_modes_map)
  File "xxx/python-thermia-online-api/ThermiaOnlineAPI/model/HeatPump.py", line 389, in <lambda>
    values.get("value"): values.get("name").split(data["valueNamePrefix"])[
IndexError: list index out of range
Gjorret commented 1 year ago

@Gjorret I assume you already see this data in available_operation_modes property, is that correct?

[
            {
                "confirmation": null,
                "isReadonly": true,
                "name": "REG_VALUE_OPERATION_MODE_OFF",
                "value": 1,
                "visible": true
            },
            {
                "confirmation": "UI_OPERATION_MODE_AUX_HEATER_ONLY_WARNING",
                "isReadonly": false,
                "name": "REG_VALUE_OPERATION_MODE_SERVICE",
                "value": 2,
                "visible": false
            },
            {
                "confirmation": "UI_OPERATION_MODE_AUX_HEATER_ONLY_WARNING",
                "isReadonly": false,
                "name": "REG_VALUE_OPERATION_MODE_AUX_HEATER_ONLY",
                "value": 2,
                "visible": true
            },
            {
                "confirmation": null,
                "isReadonly": false,
                "name": "REG_VALUE_OPERATION_MODE_AUTO",
                "value": 3,
                "visible": true
            }
        ]

Yes @klejejs, i believe so. Debug.txt attached. debug.txt

klejejs commented 1 year ago

@Gjorret Do you see available operational statuses with the new version?

klejejs commented 1 year ago

@oskari Sorry I made a mapping mistake. The new 3.8 version should fix the issue. Please try it out and report back 😉

oskari commented 1 year ago

Updated to 3.8:

Operational status
Operational status: None
Available operational statuses: ValuesView(ChainMap({1: 'COMPR'}, {8: 'HOT_WATER'}, {16: 'HEATING'}, {32: 'DEFROST'}, {128: 'COOLING'}))
Available operational statuses map: ChainMap({1: 'COMPR'}, {8: 'HOT_WATER'}, {16: 'HEATING'}, {32: 'DEFROST'}, {128: 'COOLING'})
Auxiliary heater 3KW: None
Auxiliary heater 6KW: None
Auxiliary heater 9KW: None
Auxiliary heater 12KW: None
Auxiliary heater 15KW: None
Compressor status: None
Brine pump status: None
Radiator pump status: None
Cooling status: None
Hot water status: None
Heating status: None
Integral: None

@klejejs If there's any debug or api data I can provide, just let me know!

oskari commented 1 year ago

Here's a debug.txt with deviceId and name removed: debug.txt

klejejs commented 1 year ago

@oskari Thanks for the debug file. It is hard for me to test something that I personally do not see, so excuse me for many iterations. Can you please try version 3.9 and see if it fixes the issue?

oskari commented 1 year ago

@klejejs It's okay, that's why I'm asking if you need anything more :) If you want, I can get you the json api responses from thermia online if it helps?

example.py in 3.9:

Operational status
Traceback (most recent call last):
  File "example.py", line 53, in <module>
    print("Operational status: " + str(heat_pump.operational_status))
  File "/home/oskarik/projektit/python-thermia-online-api/ThermiaOnlineAPI/model/HeatPump.py", line 573, in operational_status
    data = self.__get_all_operational_statuses_from_operational_status()
  File "/home/oskarik/projektit/python-thermia-online-api/ThermiaOnlineAPI/model/HeatPump.py", line 393, in __get_all_operational_statuses_from_operational_status
    filter(lambda value: value.get("visible"), data["registerValues"])
KeyError: 'registerValues'
klejejs commented 1 year ago

@oskari Thanks, I think I have everything, it's just that with these functions that I cannot test, I start making stupid mistakes. I should find time and write unit tests to ensure everything works correctly. In any case, the new 3.10 version should hopefully now work :).

oskari commented 1 year ago

@klejejs

Traceback (most recent call last):
  File "example.py", line 53, in <module>
    print("Operational status: " + str(heat_pump.operational_status))
  File "/home/oskarik/projektit/python-thermia-online-api/ThermiaOnlineAPI/model/HeatPump.py", line 573, in operational_status
    data = self.__get_all_operational_statuses_from_operational_status()
  File "/home/oskarik/projektit/python-thermia-online-api/ThermiaOnlineAPI/model/HeatPump.py", line 405, in __get_all_operational_statuses_from_operational_status
    operation_modes_list = list(operation_modes_map)
  File "/home/oskarik/projektit/python-thermia-online-api/ThermiaOnlineAPI/model/HeatPump.py", line 398, in <lambda>
    values.get("value"): values.get("name").split(data["valueNamePrefix"])[
KeyError: 'valueNamePrefix'
klejejs commented 1 year ago

I finally inderstand what @wouterse meant by status being a binary number :). Thanks for the tip @wouterse, I would have never figured that out. I will have to investigate how to properly implement that logic and return multiple statuses.

klejejs commented 1 year ago

@oskari Version 3.11 is released. I think (hope :)) I figured it out. You should be seeing a list of active statuses now.

oskari commented 1 year ago

Great! Looks a lot more better now:

Operational status
Operational status: ['HEATING', 'RADIATORPUMP', 'COMPR']
Available operational statuses: ValuesView(ChainMap({1: 'COMPR'}, {2: 'RADIATORPUMP'}, {8: 'HOT_WATER'}, {16: 'HEATING'}, {32: 'DEFROST'}, {64: 'POOL'}, {128: 'COOLING'}))
Available operational statuses map: ChainMap({1: 'COMPR'}, {2: 'RADIATORPUMP'}, {8: 'HOT_WATER'}, {16: 'HEATING'}, {32: 'DEFROST'}, {64: 'POOL'}, {128: 'COOLING'})
Auxiliary heater 3KW: None
Auxiliary heater 6KW: None
Auxiliary heater 9KW: None
Auxiliary heater 12KW: None
Auxiliary heater 15KW: None
Compressor status: None
Brine pump status: None
Radiator pump status: None
Cooling status: None
Hot water status: None
Heating status: None
Integral: None
Pid: 16
wouterse commented 1 year ago

Many years ago every byte used in a computer was valuable so you tried to squeeze as much information in every byte as possible. I was surprised to see that this technique was still used in this interface. Sorry for the confusing explanation of the binary value.

The code that I use is:

... mode = heat_pump.get_register_data_by_register_group_and_name("REG_GROUP_OPERATIONAL_STATUS", "COMP_STATUS")["value"] mode = mode & 0b111001 if mode == 8: heat pump_mode = "cooling" elif mode == 9: heat pump_mode = "pool" elif mode == 17: heat pump_mode = "hot water" elif mode == 33: heat pump_mode = "heating" elif heat pump_mode = "off" ....

The statement "mode = mode & 0b111001" gets rid of bit numbers 1 and 2 (bit counting starts at zero), for my heat pump these refer to the radiator pump and the brine pump. I'm not particularly interested in those. If you just want to know if the compressor (bit 0, "COMPR") is running you would use: mode = mode & 0b000001.

A few things:

in my case bit 5 (value 16) refers to hot water. I sometimes observe mode is 16 and sometimes mode is 17: 17 means that the compressor is actually switched on and and water is heated; 16 means that the compressor has not been switched on yet: the brine pump needs to run for a while and once it is switched on, the state will change to 17. Same happens for heating and pool heating. the bits have different meanings in different heat pumps, In my case HOT_WATER is 16; in Oskari's case HOT_WATER is 8. Fortunately you can get this from the Available operational statuses (map). In my case bit 3 (value 8) is used for both COOLING and POOL. Mode 9 will always mean POOL, Mode 8 may mean COOLING or POOL while the compressor has not been switched on yet.

And finally, you are already exposing the multi-value operational status. I would not know how to to present it in a better way than as e.g. ['HEATING', 'RADIATORPUMP', 'COMPR'] (taken from Askari''s message): Operational status Operational status: ['HEATING', 'RADIATORPUMP', 'COMPR'] Available operational statuses: ValuesView(ChainMap({1: 'COMPR'}, {2: 'RADIATORPUMP'}, {8: 'HOT_WATER'}, {16: 'HEATING'}, {32: 'DEFROST'}, {64: 'POOL'}, {128: 'COOLING'})) Available operational statuses map: ChainMap({1: 'COMPR'}, {2: 'RADIATORPUMP'}, {8: 'HOT_WATER'}, {16: 'HEATING'}, {32: 'DEFROST'}, {64: 'POOL'}, {128: 'COOLING'}) As an aside, I use the interface to measure the energy consumption of the heat pump. Unfortunately the heat pump does not provide this information, so I installed some current sensors. Every minute I read the operational status form the Thermia_api and get the average current (and hence wattage) for that minute. This way I know how much energy goes into heating, cooling, hot water and the pool.

Op 6 jan. 2023, om 00:40 heeft Krisjanis Lejejs @.***> het volgende geschreven:

I finally inderstand what @wouterse https://github.com/wouterse meant by status being a binary number :). Thanks for the tip @wouterse https://github.com/wouterse, I would have never figured that out. I will have to investigate how to properly implement that logic and return multiple statuses.

— Reply to this email directly, view it on GitHub https://github.com/klejejs/python-thermia-online-api/issues/14#issuecomment-1372933621, or unsubscribe https://github.com/notifications/unsubscribe-auth/AY4EXTNJ6RRELCDJB3AKZXDWQ5LWRANCNFSM6AAAAAATM5UL2E. You are receiving this because you were mentioned.

klejejs commented 1 year ago

@wouterse thanks for the detailed explanation on this. I recently studied bitwise operations at university, so it is interesting to see how they are used in a real-life scenario. I would have never thought though that something like this would be still used too. @oskari Good to hear everything is finally working. Closing this issue now, if there are any more problems, please feel free to create a new issue.

Gjorret commented 1 year ago

@Gjorret Do you see available operational statuses with the new version?

Sorry for late reply, but yes, i now see the operational status:

Operational status
Operational status: STATUS_HEAT
Available operational statuses: ValuesView(ChainMap({1: 'STATUS_MANUAL'}, {2: 'STATUS_DEFROST'}, {4: 'STATUS_HOTWATER'}, {8: 'STATUS_HEAT'}, {16: 'STATUS_COOL'}, {32: 'STATUS_POOL'}, {64: 'STATUS_LEGIONELLA'}, {128: 'STATUS_PASSIVE_COOL'}, {512: 'STATUS_STANDBY'}, {1024: 'STATUS_NO_DEMAND'}, {2048: 'OPERATION_MODE_OFF'}))
Available operational statuses map: ChainMap({1: 'STATUS_MANUAL'}, {2: 'STATUS_DEFROST'}, {4: 'STATUS_HOTWATER'}, {8: 'STATUS_HEAT'}, {16: 'STATUS_COOL'}, {32: 'STATUS_POOL'}, {64: 'STATUS_LEGIONELLA'}, {128: 'STATUS_PASSIVE_COOL'}, {512: 'STATUS_STANDBY'}, {1024: 'STATUS_NO_DEMAND'}, {2048: 'OPERATION_MODE_OFF'})
Auxiliary heater 3KW: None
Auxiliary heater 6KW: None
Auxiliary heater 9KW: None
Auxiliary heater 12KW: None
Auxiliary heater 15KW: None
Compressor status: None
Brine pump status: None
Radiator pump status: None
Cooling status: None
Hot water status: None
Heating status: None
Integral: None