MichielVanwelsenaere / HomeAutomation.CoDeSys3

Home Automation system build in CoDeSys 3 with MQTT communication to any third party Home Automation software
MIT License
112 stars 37 forks source link

Add support for MQTT Discovery in Home Assistant? #77

Closed roomboot closed 1 year ago

roomboot commented 3 years ago

Hi Michiel,

First off: I'm currently using your repository in my personal (large size) project and it's working great. Thanks!

Currently the setup of a function block is all manual, both in codesys and in hass. I think the project could benefit by making this process less cumbersome. One of the ways I can think of achieving this is by supporting the MQTT Discovery that is used in Home Assistant. This enables the user a much easier way to edit their MQTT devices, like the naming of sensors or lights.

MQTT discovery topic layout

<discovery_prefix>/<component>/[<node_id>/]<object_id>/config
Text

You can read more about it here: https://www.home-assistant.io/docs/mqtt/discovery/

I realise this is a big change. To be blunt, it's basically a rewrite of the mqtt functionblocks of the project.. What are your thoughts about this?

MichielVanwelsenaere commented 3 years ago

Hi roombot!

great to hear that it's working great for you!

I've actually considered this when starting the project but didn't spend any time on it due to the following reasons:

let me know your thoughts on all of this as well

roomboot commented 3 years ago

Hi Michiel, thanks for your thorough responses. The points you mention are all very valid.

My situation is constantly evolving through a continuous loop of improvements. I recently added some PIR sensors For example and I'm busy adding extra functionality to pushbuttons in home assistant. This means going back and forth, restarting devices etc. I suspect the proces wouldn't be much different for new users picking up the project, for the first 6 months at least.

In response to your points:

To add to this, some mqtt functionality (calling events from buttonpresses like 'short_press button_4') is locked behind discovered devices.

roomboot commented 3 years ago

I've engineered a crude prototype in node-red to understand the technology.

By sending a payload to the topic homeassistant/device_automation/FB_DI_PB_14_SINGLE/config I was able to automatically configure a device in home assistant with a 'MQTT device trigger '.

JSON payload that was send for the SINGLE trigger:

{
    "automation_type": "trigger",
    "payload": "SINGLE",
    "qos": 2,
    "topic": "WAGO-PFC100/Out/DigitalInputs/Pushbuttons/FB_DI_PB_014",
    "type": "button_short_press",
    "subtype": "FB_DI_PB_14",
    "device": {
        "identifiers": "PFC100_FB_DI_PB_14",
        "manufacturer": "CodeSys Home Automation Project",
        "model": "FB_DI_PB",
        "name": "Kamer van Joost drukknoppen",
        "sw_version": "0.0.1",
        "via_device": "PFC100"
    }
}

... and then to add the DOUBLE trigger: topic: homeassistant/device_automation/FB_DI_PB_14_DOUBLE/config payload:

{
    "automation_type": "trigger",
    "payload": "DOUBLE",
    "qos": 2,
    "topic": "WAGO-PFC100/Out/DigitalInputs/Pushbuttons/FB_DI_PB_014",
    "type": "button_double_press",
    "subtype": "FB_DI_PB_14",
    "device": {
        "identifiers": "PFC100_FB_DI_PB_14",
        "manufacturer": "CodeSys Home Automation Project",
        "model": "FB_DI_PB",
        "name": "Kamer van Joost drukknoppen",
        "sw_version": "0.0.1",
        "via_device": "PFC100"
    }
}

Results: image image

To configure a pushbutton dive with single, double and long_press you would have to send three of these config payloads. This could be done by adding a new method to the functionblock, which is optionally called right after FB.init_mqtt to configure the devices in HA. This code only has to run once per functionblock after startup.

MichielVanwelsenaere commented 2 years ago

Thank you for your reseach, it's really much appreciated; Yet, going to close this one due to limited interest, ROI and time from my end.

meijer3 commented 1 year ago

I know its closed, but my YAML is growing into alot of lines. So I looked into the discovery For now I used to sideload MQTT with a javascript. If I would create the code, would you implemenet it? (via PR or however it works with ecockpit)

{
  "~": "Devices/PLC/House",
  name: `Desk light`,
  device_class: "light",
  object_id: "FB_AO_DMX_DIMMER_003",
  unique_id: "FB_AO_DMX_DIMMER_003",
  icon: "mdi:lightbulb",
  state_topic: "~/Out/Dimmers/FB_AO_DMX_DIMMER_003/Q",
  payload_on: "TRUE",
  payload_off: "FALSE",
  brightness_state_topic: "~/Out/Dimmers/FB_AO_DMX_DIMMER_003/OUT",
  command_topic: "~/In/Dimmers/FB_AO_DMX_DIMMER_003",
  brightness_command_topic: "~/In/Dimmers/FB_AO_DMX_DIMMER_003",
  brightness_scale: 255,
  optimistic: false,
  on_command_type: "last",
  availability_topic: "~/availability",
  availability_template: "online", // online or offline
  device: {
      name: "Wago",
      configuration_url: "http://wago.ii",
      connections: [
        ["ip", "10.1.1.3"],
        ["mac", "yes:yes"],
      ],
      identifiers: "Wago-750-8207-000-1",
      manufacturer: "Wago",
      model: "750-8207-000",
      sw_version: "1.0",
   }
}
MichielVanwelsenaere commented 1 year ago

I've reopened this issue as there continues to be interest in this.

I'll gladly have a look at anything anyone contributes on this topic. At the moment the focus on my end remains on HVAC.

Some thoughts after giving the MQTT discovery docs and HA device/entity principles a good read I would suggest the following approach:

Let me know your thoughts.

meijer3 commented 1 year ago

A great to hear! I agree with all pointers. The object_id = unique_id is perfect but I still cannot figure out what I want in my setup.

I used to have option A. That works quite okay in most places in home assistant UI. But it doesn't show in YAML ( maybe it should be a feature in the yaml editor of HA-UI).

drawing

In nodered there is this popup. that helps as well, but without the popup...

drawing drawing
MichielVanwelsenaere commented 1 year ago

I suppose we can start with 'objectId = uniqueId' and if we want we can then still allow using something user provided as objectId. It sounds like a small enhancement.

If you're looking for a JSON library perhaps check this one: link

meijer3 commented 1 year ago

Hmmm I am stuck at https://github.com/stefandreyer/JSON-Library/issues/3 It does create an empty entry in mqtt

MichielVanwelsenaere commented 1 year ago

good that you've created an issue for Stefan, for sure he'll assist your further. For now, I don't have any experience with the library myself.

stefandreyer commented 1 year ago

Hi guys,

what is your goal in using discovery?

In my projects im so far that I'm just create an new instanse and can do the logic? Do you don't need to touch the PLC if discovery is working?

Or do you implement a extra configurable controler which can access discovery data on your PLC?

Let me know.

BR Stefan

MichielVanwelsenaere commented 1 year ago

Hi guys,

what is your goal in using discovery?

In my projects im so far that I'm just create an new instanse and can do the logic? Do you don't need to touch the PLC if discovery is working?

Or do you implement a extra configurable controler which can access discovery data on your PLC?

Let me know.

BR Stefan

Hi Stefan,

All we're trying to do is have some or our function blocks send a json via mqtt upon startup. The json contains a bunch of configuration properties which will cause an entity to be auto created in Home-Assistant.

So we're not trying to autocreate something in the PLC, we trying to autocreate something in an external system via an existing standard.

stefandreyer commented 1 year ago

Oh,

thats easy, allready done something like this.

Questions?

BR Stefan

meijer3 commented 1 year ago

Yes I do have some questions.

  1. I got an empty string back, but we can discuss that at your repo

  2. Is there a way to increase the STRING length? The URLs are quite long\ image

  3. To limit the URLS, in Home assistant MQTT there is an option to use a key ~. But that is not a valid for a JSONVAR. Is there away to define it with a different name foobar, but overule the final key to ~? My backup is, I didnt check yet, to use e.g. the name replace_this, and do a replace on the final string. image

stefandreyer commented 1 year ago

Hi,

to 2. you can Increase the string length in your projekt in Library parameters of the lib:

image

to 3.

extend this method of the lib for your requerments: image

like: IF name = "HomeAssistantKey" then name := '~' end_if

BR Stefan

meijer3 commented 1 year ago

I think I have most of the code now. 'Device' is in an extra key but Stefan is checking that. I do have an other question remarking the 'device'. Home assistant excepts an array in an array. cns: [["ip", "10.1.1.5"],["mac", "00-30-AA-AA-AA-AA"],], Its probably me but my current settup is not working

cns: ARRAY[1..3] OF ARRAY[1..2] OF JSONVAR;
---
THIS^.device.cns[1][1].CharString := 'ip';
THIS^.device.cns[1][2].CharString := sIpAddr
stefandreyer commented 1 year ago

Hi,

i dont' know if the lib support anonymus arrays in arrays, I'll have to check.

BR Stefan

stefandreyer commented 1 year ago

Hi, a workaround wold be to place a named struct an after compose remove this name out of the string.

Tim(creator of this lib) didn't answer until now.

BR Stefan

meijer3 commented 1 year ago

An anonymous array in an array is not possible with renaming. It already created a object + key [ { tobeRemoved : [] } ] would result in something invalid like [ { : [] } ] Maybe tim can look in this aswell?

MichielVanwelsenaere commented 1 year ago

Hi, a workaround wold be to place a named struct an after compose remove this name out of the string.

Tim(creator of this lib) didn't answer until now.

BR Stefan

Hi Stefan, I was thinking we could insert something in the struct value like "toBeReplaced" and then -obivously- replace it afte the json string has been generated.

Problem is that the replace function offered by Codesys has a 255 chars limitation while the json string has a max of 1500 chars. Any idea how to work around this?