WTH isn’t UDP comms integrated in HA?

UDP is an essential comms protocol used by many devices. On my computers I run a python script that listens and sends on certain UDP ports.

As I know it’s easy to include UDP binding and sending in Python I wonder why it isn’t part of the HA integration.

Now I have to send my commands to netcat via a service call to shell command. Which works, sort of. When you have no variables in your string. Or else the shell command breaks when it reaches the pipe symbol.

I’ve been trouble shooting this a week long and gave up. I want to send things like a volume slider to a UDP listener so I can control the volume of that device. That’s not possible now.

It would also be neat if HA could respond to incoming UDP msg’s of course.

It blows my mind that this isn’t integrated and I have to resort to half broken commands.

Probably because mqtt with qos 0 is easier to implement as there is a broker addon.

They are not mutually exclusive.

Lots of professional AVL gear works with UDP because TCP/IP is slower and time is more important than ACK.

As HA is treading into show control waters it would be great if such implementations were to be made. I’m not saying I will be using HA professionally but I do want to use my scripts and ((semi )pro) devices at home :slight_smile:

Receiving syslog for state information is another use of receiving and processing udp.

The current syslog integration is for sending notifications.

I wish I could vote more than once for this!

Lack of a UDP integration seems like an oversight considering there’s an integration for TCP and one for serial communications.

Currently, you can use Node-red to receive UDP and then forward it to Home Assistant via an appropriate service call (or via MQTT). However, a native integration would require less infrastructure (especially when you consider how little code is needed to receive a lightweight protocol like UDP).

2 Likes

Alright, because I’m normally typing on a phone I have now transitioned to a computer and will explain why this bothers me. For people on Tweakers this wil be kind of a cross post (not a literal copy but the same errors addressed). I’m sorry for that.

So. The workaround for sending UDP works. Kinda. This works:

shell_command:
  send_smth: "echo -n 'hello world' | nc -u -w1 192.168.123.1 54321"

This also works (shell_command > send_vol):

"echo -n 'volume 100' | nc -u -w1 192.168.12.34 65432"

and

'echo -n "volume 100" | nc -u -w1 192.168.12.34 65432'

So far so good. But then try to send a slider state and trouble begins.
I have tried so many workarounds by putting the slider value in an input_text or what not so this will hopefully work but the thing is that when combining the string and the formatting there is no way to escape the pipe ("|") which breaks the command.

A few options I have tried (most of them I already tossed out but these are still commented out in my script as there were reeaally close to the solution but no cigar:

[1]

shell_command:
  send_vol: 'echo -n {{ states("input_text.tekstveld") }}'

where input_text.textveld is a string containing the slider value as a string and the remaining command; | nc -u -w1 192.168.123.1 54321

And these:

[2]

shell_command:
  send_vol: "{{ states('input_text.tekstveld') }}"

Where I have the complete string into the text field.

And all these iterations of send_vol:

[3]

'echo -n volume {{ states("input_number.vol_bak") }} | nc -u -w1 192.168.123.1 54321'

"echo -n 'volume {{ vol_data }}' | nc -u -w1 192.168.123.1 54321"

'echo -n ''volume {{ vol_data }}'' | nc -u -w1 192.168.123.1 54321'

'echo -n ''volume {{ (states('input_number.vol_bak').split('.')[0]) }}'' | nc -u -w1 192.168.123.1 54321'

"echo -n 'volume {{states(''input_number.vol_bak'')}}' | nc -u -w1 192.168.123.1 54321"

"echo -n 'volume {{ states(''input_text.tekstveld'') }}' | nc -u -w1 192.168.12.34 54321"

Where:

vol_data = the slider value split and set to txt string
input_number.vol_bak = the actual slider defined in automations.yaml which is defined as follows:

[4]

input_number:
  vol_bak:
    name: "Volume Mediabak"
    initial: 0
    min: 0
    max: 100
    step: 1

input_text.tekstveld = only the value in this instance ([3]) as seen in the automations.yaml during that phase as:

[5]

- id: 'xxxxxxxxxxx'
  alias: Volume mediabak
  trigger:
  - entity_id: input_number.vol_bak
    platform: state
  action:
  - service: shell_command.volume_mediabak
    data_template:
      vol_data: "{{ '%s' % states('input_number.vol_bak') }}"
  - service: input_text.set_value
    data_template: 
      entity_id: input_text.tekstveld
      value: "{{'%s' % (states('input_number.vol_bak').split('.')[0]) }}"

FROM HERE ON I WILL GO BACK IN TIME TO MY BACK TRACKING THE ISSUE ON ANOTHER FORUM:

I have tried amongst others, these, in templates (dev tools), a few of which I knew wouldn’t work but tried just to falsify:

[6a]

'echo -n "volume {{ states('input_number.vol_bak').split('.')[0] }}" | nc -u -w1 192.168.12.34 54321'
'echo -n "volume {{ states('input_number.vol_bak').split('.')[0] }}" | nc -u -w1 192.168.12.34 54321'
"echo -n "volume {{ states('input_number.vol_bak').split('.')[0] }}" | nc -u -w1 192.168.12.34 54321"
"echo -n ''volume {{'%s' % (states('input_number.vol_bak').split('.')[0]) }}'' | nc -u -w1 192.168.12.34 54321"
"echo -n ''volume {{ states('input_number.vol_bak').split('.')[0] | int }}'' | nc -u -w1 192.168.12.34 54321"
"echo -n ''volume {{ states('input_number.vol_bak').split('.')[0] }}'' | nc -u -w1 192.168.12.34 54321"
"echo -n ''volume {{ states('input_text.tekstveld') }}'' | nc -u -w1 192.168.12.34 54321"
{{ states("input_number.vol_bak") }}

{{ states("input_number.vol_bak").split('.')[0] }}

 "echo -n 'volume {{ states('vol_data') }}' | nc -u -w1 192.168.12.34 54321"
 "echo -n 'volume {{ vol_data }}' | nc -u -w1 192.168.12.34 54321"

The test template outcome was:

[6b]

'echo -n "volume 80" | nc -u -w1 192.168.12.34 54321'
'echo -n "volume 80" | nc -u -w1 192.168.12.34 54321'
"echo -n "volume 80" | nc -u -w1 192.168.12.34 54321"
"echo -n ''volume 80'' | nc -u -w1 192.168.12.34 54321"
"echo -n ''volume 80'' | nc -u -w1 192.168.12.34 54321"
"echo -n ''volume 80'' | nc -u -w1 192.168.12.34 54321"
"echo -n ''volume 80'' | nc -u -w1 192.168.12.34 54321"
80.0

80

 "echo -n 'volume unknown' | nc -u -w1 192.168.12.34 54321"
 "echo -n 'volume ' | nc -u -w1 192.168.12.34 54321"

So testing and testing I found the root cause of these not working:

[7a]

"echo -n 'volume {{ (states('input_number.vol_bak').split('.')[0]) }}'  | nc -u -w1 192.168.12.34 65432"
"echo -n ''volume {{ (states('input_number.vol_bak').split('.')[0]) }}''  | nc -u -w1 192.168.12.34 65432"
"echo -n \'volume {{ (states('input_number.vol_bak').split('.')[0]) }}\'  | nc -u -w1 192.168.12.34 65432"
"echo -n 'volume {{ (states(''input_number.vol_bak'').split(''.'')[0]) }}'  | nc -u -w1 192.168.12.34 65432"
"echo -n 'volume {{ (states('input_number.vol_bak').split('.')[0]) }}'  | nc -u -w1 192.168.12.34 65432"
"echo -n ""volume {{ (states('input_number.vol_bak').split('.')[0]) }}""  | nc -u -w1 192.168.12.34 65432"
"echo -n \"volume {{ (states('input_number.vol_bak').split('.')[0]) }}\"  | nc -u -w1 192.168.12.34 65432"

Is this error:

[7b]

2020-07-30 21:44:22 DEBUG (MainThread) [homeassistant.components.shell_command] Stdout of command: `echo -n 'volume {{ (states('input_number.vol_bak').split('.')[0]) }}' | nc -u -w1 192.168.12.34 65432`, return code: 0:
b'volume 47 | nc -u -w1 192.168.12.34 65432'

Which couldn’t be helped by using triple quotes either, because:

[7c]

Error loading /config/configuration.yaml: while parsing a block mapping
  in "/config/configuration.yaml", line 51, column 3
expected <block end>, but found '<scalar>'
  in "/config/configuration.yaml", line 57, column 22

Or even using escapes in front of the quotes (’ {data}’):

Error loading /config/configuration.yaml: while scanning a double-quoted scalar
  in "/config/configuration.yaml", line 57, column 20
found unknown escape character "'"
  in "/config/configuration.yaml", line 57, column 30

So I tried the quotes in formatting too:

[8a]

"echo -n {{'\"volume ' + (states('input_number.vol_bak').split('.')[0]) + '\"' }} | nc -u -w1 192.168.12.34 65432"

Which leads to

[8b]

2020-07-30 22:14:40 DEBUG (MainThread) [homeassistant.components.shell_command] Stdout of command: `echo -n 'volume {{ (states('input_number.vol_bak').split('.')[0]) }}' | nc -u -w1 192.168.12.34 65432`, return code: 0:
b'volume 79 | nc -u -w1 192.168.12.34 65432'

So then a double check if the automation var will not work:

[9a]

"echo -n {{'\"volume ' + vol_data + '\"' }} | nc -u -w1 192.168.12.34 65432"

Nope:

[9b]

2020-07-30 22:24:17 DEBUG (MainThread) [homeassistant.components.shell_command] Stdout of command: `echo -n {{'"volume ' + vol_data + '"' }} | nc -u -w1 192.168.12.34 65432`, return code: 0:
b'volume 68 | nc -u -w1 192.168.12.34 65432'

So then I checked all my previous Errors and suddenly I saw the light that the real issue is parsing. You actually see that shell command is echoing, right? It’s just not piping but actually echoing back the string to HA!

An extra check of this, now using vol_data completely parsed and ready to be sent out as a string:

Automations.yaml

- id: '1234567891011'
  alias: Volume mediabak
  trigger:
  - entity_id: input_number.vol_bak
    platform: state
  action:
  - service: input_text.set_value
    data_template:
      entity_id: input_text.tekstveld
      value: 'echo -n volume {{ states("input_number.vol_bak").split(".")[0] }} | nc -u -w1 192.168.12.34 65432'
  - service: shell_command.volume_mediabak
    data_template:
      vol_data: "{{ states('input_text.tekstveld') }}"
  mode: single

configuration.yaml

shell_command:
  volume_mediabak: "echo -n {{vol_data}}"

You see what I did there? Double echo :slight_smile:

Gives:

2020-07-31 12:30:14 DEBUG (MainThread) [homeassistant.components.shell_command] Stdout of command: `echo -n {{vol_data}}`, return code: 0:
b'echo -n volume 18 | nc -u -w1 192.168.12.34 65432'

So try this again without the extra echo:

shell_command:
  volume_mediabak: "{{vol_data}}"

Gives:

2020-07-31 12:31:41 DEBUG (MainThread) [homeassistant.components.shell_command] Stderr of command: `{{vol_data}}`, return code: 127:
b'/bin/sh: {{vol_data}}: not found\n'
2020-07-31 12:31:41 ERROR (MainThread) [homeassistant.components.shell_command] Error running command: `{{vol_data}}`, return code: 127
NoneType: None

Close, but no cigar.

So there we are. I have been wasting weeks of my time to accomplish something that is ridiculously simple to make in python. It’s no more than three lines of code. But this ‘workaround’ with netcat blows serious chunks.

Could you write this python and make it a custom component as a stepping stone to a core PR?
Even a python_script approach would be better than all the quoting and escaping above.

Install Node RED. That was my solution and I haven’t looked back since. :slight_smile:

Is this issue related to pipe and send netcat? I also have a TCP server, I am sending tcp commands too, with the value of an input slider ( brightness )

I was also struggling with the piping and jinja, but I solved it eventually

I’m not sure you know that udp doesn’t guarantee packet delivery nor their original order. I wouldn’t use it for mission critical home automation purposes.

We are talking about transport layer. MQTT is using tcp with ensures delivery or reports timeout instead (which is not case for udp)

i do it like this , my .py file does the actual netcat command

dimmer-: python3 /config/python_scripts/dimmer.py 00 {{ '%02.f'%((((states('input_number.dimmer1') | float / 255 ) * 90) // 10) * 10) }}