the78mole / esphome_components

ESPhome Components from the little digger
Other
24 stars 11 forks source link

Sending command to the Buderus control unit #4

Closed Bascht74 closed 1 year ago

Bascht74 commented 1 year ago

Hi, (in German below) first of all thank you @the78mole and @jensgraef for making this result possible! I now have the existing sensors in operation and it works perfectly.

I will try to increase the amount of binary sensors and sensors over the weekend, so that even more meaningful data comes in. We could also distinguish between normal sensors (e.g. measured values) and "diagnostic" sensors (e.g. set points).

Now back to the point: Currently, the loop routine does not allow you to write a command to the Buderus. Currently, this is only done once during "Setup" (0xEE, 0x00, 0x00 only). I have already tried a few things with "my skills", but have always failed (with my abilities).

My question now is whether this could be built in, at least rudimentarily. It would be completely sufficient if one could execute a command in ESPHome such as km271.send: {0x07, 0x00, 0x65, 0x65, 0x65, 0x00, 0x65} or km271.send: 07006565650065, which in fact would work like the setup command (it adds 10 03 & BCC and then sends it analogue to the Buderus).

Alternatively, one could also include input, switch and select entities. If there could be an example here, I, myself could try to extend this example to more config sensors.

This would make it possible to do things with ESPHome automatisations like

All things that can help save heating costs - especially in the transitional period and at the same time bring some comfort.

As I said, a simple way to send a telegram would be sufficient for that. Solving it via switches, input & select entities would of course be a dream!


Hi, erstmal vielen Dank, dass @the78mole und @jensgraef dieses Projekt hier möglich gemacht haben! Ich habe inzwischen viele Sensoren in Betrieb und es funktioniert einwandfrei.

Ich werde versuchen, am Wochenende noch die Menge der binary sensors und sensors zu ehöhen, so dass noch mehr sinnvolle Daten kommen (= Fleißarbeit, die man auch ohne riesige Programmierkenntnisse erledigen kann). Zudem könnten wir noch die bestehenden Sensoren zwischen normalen Sensoren (z.B. für Meßwerte) und "diagnostic" Sensoren (z.B. set points) unterscheiden.

Jetzt wieder zur Sache: Aktuell lässt die Loop-Routine es nicht zu, dass man einen Befehl an die Buderus schreiben kann. Derzeit wird das nur einmal beim "Setup" gemacht (0xEE, 0x00, 0x00 ). Ich habe mit "Board-Mitteln" schon ein paar Dinge versucht, bin aber (mit meinen Fähigkeiten) immer gescheitert.

Meine Frage wäre nun, ob das zumindest rudimentär eingebaut werden könnte. Es würde zunächst einmal völlig ausreichen, wenn man in ESPHome z.B. einen Befehl wie km271.send: {0x07, 0x00, 0x65, 0x65, 0x65, 0x65, 0x00, 0x65} oder km271.send: 0700656565650065 ausgeführen könnte, der faktisch wie der Setup-Befehl funktioniert (d.h. es wird 10 03 & BCC ergänzt und dann analog 0xEE, 0x00, 0x00 an die Buderus geschickt.

Alternativ könnte man auch input, switch und select Entitäten einbauen. Wenn es hier ein Beispiel gäbe, dann könnte ich das analog der Sensoren versuchen auszubauen (= Fleißarbeit, die man auch ohne riesige Programmierkenntnisse erledigen kann).

Dadurch wäre es möglich, über ESPHome-Automationen Dinge wie

Alles Dinge, die die Heizkosten sparen helfen - insbesondere in der Übergangszeit und gleichzeitig etwas Komfort bringen können.

Wie gesagt, eine einfache Möglichkeit, ein Telegramm zu schicken, wäre für das bereits ausreichend. Es über Switche, Input & Select Entitäten zu lösen, wäre natürlich ein Träumchen!

Ich hoffe, dass zumindest die einfache Variante noch eingebaut werden kann. Dann wäre das neben dem reinen Datenloggen auch aktive Eingriffe zum Energiesparen drin, ohne das der Komfort leidet.

the78mole commented 1 year ago

Hi @Bascht74,

you are right, sending of the command need to be timed within the communication flow. Therefore, it would be necessary to build a queue for the commands, to be filled by the events of e.g. switches and pulled by the communication loop/state machine.

But since the ESP has already populated most of the (4MB) flash, the first thing to do is to find the best board with 8 MB (this is what's soldered on the KM271-WiFi module). Could be a perfect task for you 😁. For me it will be hard to support before end of the year due to heavy work load.

Thanks for raising this issue 😁

Regards, Daniel

Bascht74 commented 1 year ago

Hi, Thx for the quick response!

For me right now, there is lots of free flash memory, so no 8 MB version needed...:

RAM:   [=         ]  14.9% (used 48772 bytes from 327680 bytes)
Flash: [======    ]  58.1% (used 1066942 bytes from 1835008 bytes) --> 50% from the 4 MB ESP32 module

As these command are set not at the same second, a simple "queue" until the next incoming 0x02 from the buderus (as with the setup routine) could be enough as it can be checked later if it is set OK.

It would even be OK to call the function via lambda, just a very minimal solution. Even "pause" the km271 module could be a solution as you can write it via lambda uart.write. But right now the DLT (0x10) is sent right away so there is no possibility to "inject" your own 0x02 :-).

I hope for a miracle or that @jensgraef has another idea (and minimal time for that) ... as you said that don't have time for it before the end of the year (according to your blog you are occupied in your basement :-) And that is totally fine for me...

I hope it is at least OK, if I do a pull request to add more binary sensors and more sensors. I think my skills a sufficient enough to do this diligent work)

the78mole commented 1 year ago

For sure, pull requests are welcome ;-)

jensgraef commented 1 year ago

Hi @Bascht74, it's nice to have you here - and thanks @the78mole for the invitation! I'm also currently dreaming of some of the features you envision, especially controlling warm water. I find it really wasteful to heat all that warm water if there's nobody around who wants to use it for the next hour or two.

I'd like to go the full way and expose inputs/switches to home assistant. The queue and the code for handling the flow is already there - currently the queue (in Writer3964R) is only one deep. So if you want to write a telegram, use

writer.enqueueTelegram(commandPayload, lengthOfCommandPayload); As long as there are no other commands currently enqueued, the code should take care of the rest.

My plan on how to continue:

Start with one value to see if everything comes together. I'd chose warm water temperature for the legionella progrogram.

  1. Add the input and verify it by just logging changed values
  2. Identify the configuration setting in the heater
  3. Add code to read the current value of the configuration setting and provide it to the input - verify it by checking in home assistant
  4. Add the code that uses enqueTelegram of the Writer3964R to write changed values back to the heater. Then changing the value via home assistant should work most of the time
  5. Increase the queue size of Writer3964R so writing multiple values in quick succession will not fail due to limited queue size.

If this works as planned, add support for more writable values. Currently my time is also quite restricted. I'll think I'll be working on in some more at the weekend - but no promises. If you (or anyone else) likes to tackle one of the steps above, feel free to do it and create a pull request.

Adding more sensor values would also be nice - I just added the ones I'd like to see most :)

Bascht74 commented 1 year ago

For sure, pull requests are welcome ;-)

I just dared and made a pull request.

Bascht74 commented 1 year ago

Hi @Bascht74, it's nice to have you here - and thanks @the78mole for the invitation! I'm also currently dreaming of some of the features you envision, especially controlling warm water. I find it really wasteful to heat all that warm water if there's nobody around who wants to use it for the next hour or two.

Yes! Just do it once a day and control the circulation.

I'd like to go the full way and expose inputs/switches to home assistant. The queue and the code for handling the flow is already there - currently the queue (in Writer3964R) is only one deep.

Great!

So if you want to write a telegram, use writer.enqueueTelegram(commandPayload, lengthOfCommandPayload); As long as there are no other commands currently enqueued, the code should take care of the rest.

I tried that, but so far no success:

button: 
  - platform: template
    id: test
    name: "test"
    on_press:
      - lambda:
          uint8_t logCommand[] = {0xEE, 0x00, 0x00};
          writer.enqueueTelegram(logCommand[, 3);

compile output:

Compiling /data/heizung/.pioenvs/heizung/src/main.cpp.o
/config/esphome/heizung.yaml: In lambda function:
/config/esphome/heizung.yaml:111:50: error: 'writer' was not declared in this scope
           uint8_t logCommand[] = {0xEE, 0x00, 0x00};
                                                  ^
*** [/data/heizung/.pioenvs/heizung/src/main.cpp.o] Error 1
========================= [FAILED] Took 10.94 seconds =========================

My plan on how to continue: Start with one value to see if everything comes together. I'd chose warm water temperature for the legionella progrogram.

Yes! Fine for me!

Add the input and verify it by just logging changed values Identify the configuration setting in the heater

If the response ist 0x10 then it is OK. That is what I learned so far. But double check might be a good idea. How do you plan to "log" the changed values after setting it? I saw the telegrams only after 0xEE, 0x00, 0x00 coming in...

Add code to read the current value of the configuration setting and provide it to the input - verify it by checking in home assistant

This code migh be usefull for some more sensors that we are not able to process right now (boleans packed in a byte)

If this works as planned, add support for more writable values.

here I could help (maybe)

Currently my time is also quite restricted. I'll think I'll be working on in some more at the weekend - but no promises.

I keep my fingers crossed...

If you (or anyone else) likes to tackle one of the steps above, feel free to do it and create a pull request.

As I am not a programmer I cannot. But if it is not too complicated to read I can adapt and do some diligent stuff for the rest of the sensors.

Adding` more sensor values would also be nice - I just added the ones I'd like to see most :)

Just did that, found some small mistakes and unified the order of the entries in every file. I don't know if my skills were sufficient for that, but I will see soon. What I also did is a correction of fhe room temperature as it needs to be devided by two. I am not sure if "außentemperatur" will work below 0° Celsius, I think one bit must be "+" and "-"... ... and I didn't find any solution (too poor skills) for the some binary sensors that must be created out of one byte that covers mulitple sensors (one bit per sensor). E.g.

    BW1WW     = 0x8424, //: "Betriebswerte 1 WW"
    BW2WW     = 0x8425, //: "Betriebswerte 2 WW"

As to https://github.com/dewenni/ESP_Buderus_KM271/blob/main/src/km271.cpp they have interesting data as well:

    case 0x8424:
      tmpState.HotWaterOperatingStates_1 = data[2];                       // 0x8424 : Bitfield
      mqttPublish(addTopic("/status/WW_BW1_Automatik"), String(bitRead(tmpState.HotWaterOperatingStates_1, 0)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW1_Desinfektion"), String(bitRead(tmpState.HotWaterOperatingStates_1, 1)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW1_Nachladung"), String(bitRead(tmpState.HotWaterOperatingStates_1, 2)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW1_Ferien"), String(bitRead(tmpState.HotWaterOperatingStates_1, 3)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW1_Fehler_Desinfektion"), String(bitRead(tmpState.HotWaterOperatingStates_1, 4)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW1_Fehler_Fuehler"), String(bitRead(tmpState.HotWaterOperatingStates_1, 5)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW1_Fehler_WW_bleibt_kalt"), String(bitRead(tmpState.HotWaterOperatingStates_1, 6)).c_str(), false);        
      mqttPublish(addTopic("/status/WW_BW1_Fehler_Anode"), String(bitRead(tmpState.HotWaterOperatingStates_1, 7)).c_str(), false);   
      break;

    case 0x8425:
      tmpState.HotWaterOperatingStates_2 = data[2];                       // 0x8425 : Bitfield
      mqttPublish(addTopic("/status/WW_BW2_Laden"), String(bitRead(tmpState.HeatingCircuitOperatingStates_1, 0)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW2_Manuell"), String(bitRead(tmpState.HotWaterOperatingStates_2, 1)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW2_Nachladen"), String(bitRead(tmpState.HotWaterOperatingStates_2, 2)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW2_Ausschaltoptimierung"), String(bitRead(tmpState.HotWaterOperatingStates_2, 3)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW2_Einschaltoptimierung"), String(bitRead(tmpState.HotWaterOperatingStates_2, 4)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW2_Tag"), String(bitRead(tmpState.HotWaterOperatingStates_2, 5)).c_str(), false);
      mqttPublish(addTopic("/status/WW_BW2_Warm"), String(bitRead(tmpState.HotWaterOperatingStates_2, 6)).c_str(), false);        
      mqttPublish(addTopic("/status/WW_BW2_Vorrang"), String(bitRead(tmpState.HotWaterOperatingStates_2, 7)).c_str(), false);   

If you give me one example how to implement those I can do the rest... :-)

jensgraef commented 1 year ago

Thanks for the pull request! I'll have a better look when I've got some more time on my hands. In the mean time: I'd had a quick look into your lambda call. To get it running, you'll need to do (at least) the following two things:

  1. change the lambda from writer.enqueue... to budoil->writer.enqueue....
  2. Allow external access to the write by changing km217.h and moving Writer3964R writer; from almost at the end of the file into the public section above, e.g. directly below void on_shutdown(); near line 50.

I've also uploaded my attempt at adding a switch toggling the warm water heating from between "auto" and "night". It is not yet anywhere where I'd like to have it or tested at all. But maybe there's already something useful for you in there.

Bascht74 commented 1 year ago

I've also uploaded my attempt at adding a switch toggling the warm water heating from between "auto" and "night". It is not yet anywhere where I'd like to have it or tested at all. But maybe there's already something useful for you in there.

Will look into it. From my perspective this should be more like a select entity or - at the long end - even a climate entity (as there is no water_heater in ESPHome)

Bascht74 commented 1 year ago
  • change the lambda from writer.enqueue... to budoil->writer.enqueue....
  • Allow external access to the write by changing km217.h and moving Writer3964R writer; from almost at the end of the file into the public section above, e.g. directly below void on_shutdown(); near line 50.

Thx. Will do. This is stuff where I can get lost...

--- edit ---

It works! Thank you!!!!! So I can change completly from my own stuff to this component.

Bascht74 commented 1 year ago

I've also uploaded my attempt

@jensgraef Can you tell me "where"? I didn't find it in your branches...

jensgraef commented 1 year ago

@Bascht74 Great to hear, that it works!

The code I'm working on is here: https://github.com/the78mole/esphome_components/tree/write-warm-water-target-temperature I've tested in this morning, it switches warm water between auto and night mode.

I'm fully with you on making this a select or even a climate component. Getting a switch to run was much easier for me to begin with and see whether it works at all.

Bascht74 commented 1 year ago

@jensgraef This is what I got so far with my knowledge (see below) These sensors are not checking the result (so optimistic: true) ... and it is suffering from the write queue of "1". So a lot of times the command will not be send because the Buderus is sending a telegram and the queue is processing that.

I hope you found / will find some free time this weekend...

From my point of view these are the priorities: Prio 1: Expand queue -> Be able to really send commands, you can use template select & numbers entities for now Prio 2: Be able to process a single byte of a specific telegram and use that for a sensor -> Be able to read configuration telegrams Prio 3: Add select and number entities -> discard template entities Prio 4: Be able to process a single bit of a byte of a specific telegram and use that for a sensor -> read "Betriebswerte" of HK1, WW and so on.

So far I found out the WW temperature reading cannot be right and the Buderus is loading lots of useless heat in it. Ordered a new temp sensor...

select:
  - platform: template
    name: "Warmwasser Betriebsart"
    id: warmwasser_betriebsart
    entity_category: config
    optimistic: true
    options:
      - Dauerhaft aus (0)
      - Dauerhaft ein (1)
      - Automatik (2)
    initial_option: Automatik (2)
    set_action: 
      - lambda:
          auto index = id(warmwasser_betriebsart).active_index();
          if (index.has_value()) {
            uint8_t command[] = {0x0C, 0x0E, (uint8_t)index.value(), 0x65, 0x65, 0x65, 0x65, 0x65};
            budoil->writer.enqueueTelegram(command, 8);
          }
  - platform: template
    name: "Heizung Betriebsart"
    id: heizung_betriebsart
    entity_category: config
    optimistic: true
    options:
      - manuell Nacht (0)
      - manuell Tag (1)
      - Automatik (2)
    initial_option: Automatik (2)
    set_action: 
      - lambda:
          auto index = id(warmwasser_betriebsart).active_index();
          if (index.has_value()) {
            uint8_t command[] = {0x07, 0x00, 0x65, 0x65, 0x65, 0x65, (uint8_t)index.value(), 0x65};
            budoil->writer.enqueueTelegram(command, 8);
          }
  - platform: template
    name: "Warmwasser Zirkulation"
    id: warmwasser_zirkulation
    entity_category: config
    optimistic: true
    options:
      - dauerhaft aus
      - 1x pro Stunde
      - 2x pro Stunde
      - 3x pro Stunde
      - 4x pro Stunde
      - 5x pro Stunde
      - 6x pro Stunde
      - dauerhaft an
    initial_option: 1x pro Stunde
    set_action: 
      - lambda:
          auto index = id(warmwasser_betriebsart).active_index();
          if (index.has_value()) {
            uint8_t command[] = {0x0C, 0x0E, 0x65, 0x65, 0x65, 0x65, 0x65, (uint8_t)index.value()};
            budoil->writer.enqueueTelegram(command, 8);
          }
  - platform: template
    name: "Sommer ab"
    id: sommer_ab
    entity_category: config
    optimistic: true
    options:
      - Sommer
      - 10 °C
      - 11 °C
      - 12 °C
      - 13 °C
      - 14 °C
      - 15 °C
      - 16 °C
      - 17 °C
      - 18 °C
      - 19 °C
      - 20 °C
      - 21 °C
      - 22 °C
      - 23 °C
      - 24 °C
      - 25 °C
      - 26 °C
      - 27 °C
      - 28 °C
      - 29 °C
      - 30 °C
      - Winter
    initial_option: 18 °C
    set_action: 
      - lambda:
          auto index = id(warmwasser_betriebsart).active_index();
          if (index.has_value()) {
            uint8_t command[] = {0x07, 0x00, 0x65, (uint8_t)index.value(), 0x65, 0x65, 0x65, 0x65};
            budoil->writer.enqueueTelegram(command, 8);
          }

number:
  - platform: template
    name: "Warmwasser Temperatur"
    id: warmwasser_Temperatur
    entity_category: config
    optimistic: true
    min_value: 30
    max_value: 65
    step: 1
    restore_value: true
    initial_value: 50
    unit_of_measurement: "°C"
    set_action:
      - lambda:
          uint8_t command[] = {0x0C, 0x07, 0x65, 0x65, 0x65, (uint8_t)x, 0x41, 0x65};
          budoil->writer.enqueueTelegram(command, 8);
  - platform: template
    name: "HK1 Aussenhalt ab"
    id: hk1_aussenhalt_ab
    entity_category: config
    optimistic: true
    min_value: 0 # eigentlich -20, aber wie werden Werte < 0 verschlüsselt?
    max_value: 10
    step: 1
    restore_value: true
    initial_value: 18
    unit_of_measurement: "°C"
    set_action:
      - lambda:
          uint8_t command[] = {0x07, 0x15, 0x65, 0x65, (uint8_t)x, 0x65, 0x65, 0x65};
          budoil->writer.enqueueTelegram(command, 8);
  - platform: template
    name: "HK1 Frost ab"
    id: hk1_frost_ab
    entity_category: config
    optimistic: true
    min_value: 0 # eigentlich -20, aber wie werden Werte < 0 verschlüsselt?
    max_value: 10
    step: 1
    restore_value: true
    initial_value: 18
    unit_of_measurement: "°C"
    set_action:
      - lambda:
          uint8_t command[] = {0x07, 0x31, 0x65, 0x65, 0x65, 0x65, 0x65, (uint8_t)x};
          budoil->writer.enqueueTelegram(command, 8);
  - platform: template
    name: "Urlaub"
    id: urlaub
    entity_category: config
    optimistic: true
    min_value: 0
    max_value: 99
    step: 1
    restore_value: true
    initial_value: 0
    unit_of_measurement: "Tage"
    set_action:
      - lambda:
          uint8_t command[] = {0x11, 0x00, 0x65, 0x65, 0x65, (uint8_t)x, 0x65, 0x65};
          budoil->writer.enqueueTelegram(command, 8);
jensgraef commented 1 year ago

Hi @Bascht74!

I'm happy that you were able to find a sensor fault using this piece of software. Thanks for the configuration file and all the ideas. I think I'll split this issue here into multiple smaller ones for the different ares of improvement. I'll have a look into the expanding the queue tomorrow. For now I've worked on the warm water branch and now have a working switch to toggle between "auto" and "nacht" for warm water and a working number input for the warm water target temperature. Futhermore I've added a way to read only a single byte of a specific telegram (your prio 2) as this was needed for the warm water temperature. I'll be cleaning this code up a little and then merge it into main after I've got some sleep :)

jensgraef commented 1 year ago

@Bascht74 I've created some issues to track progress on your suggestions

From my point of view these are the priorities: Prio 1: Expand queue -> Be able to really send commands, you can use template select & numbers entities for now

I'll have a look into this and add more details in #7.

Prio 2: Be able to process a single byte of a specific telegram and use that for a sensor -> Be able to read configuration telegrams

I've implemented this partially in #6. With that single bytes can be read and interpreted. What's missing is proving values for multiple sensors from a single telegram.

Prio 3: Add select and number entities -> discard template entities

I've added a number entity in #6. Min and max values not yet perfect. For the select entity I've created #8 .

Prio 4: Be able to process a single bit of a byte of a specific telegram and use that for a sensor -> read "Betriebswerte" of HK1, WW and so on.

I've created issue #9 to track progress on that.

What do you think about closing this issue and continuing our discussion in the smaller ones? Feel free to create more issues for stuff I've overlooked. While I don't think I'll have the time to work on all of those I'll be happy to provide tips and discuss solutions.

the78mole commented 1 year ago

Since the more specific topics are tracked in #6 , #7 , #8 , #9 , I'll close this issue now.

Bascht74 commented 1 year ago

Yes! Great!

Bascht74 commented 1 year ago

I will add an entry for climate entitles for HK1, HK2 and WW that will make it easier to control them. To bad that there is no water heater within esphome (HA got it)