Impossible to create string of numbers starting with '+'

I’m trying to use ha-sip.answer in my automation, which registers ongoing calls as ‘+316<SomePhoneNumber>’. However, if I read in the calling number from the webhook to a variable, it seems to be converted to a number automatically, making it so the call is no longer found since the ‘+’ is removed.

Things I have tried:

actions:
  - variables:
      caller_raw: "{{ trigger.json.caller }}"
      parsed_caller: "{{  trigger.json.parsed_caller }}"
      caller_number: "{{ '\u200b​​​' ~ caller_raw.split(':')[1].split('@')[0] }}"
      caller_number1: "{{ caller_raw.split(':')[1].split('@')[0] }}"
      caller_number2: "\"{{ caller_raw.split(\":\")[1].split(\"@\")[0].strip(\" >\\\"\") }}\""
      caller_number3: "{{ caller_raw.split(':')[1].split('@')[0] | string }}"
      caller_number4: "{{ ' '+caller_raw.split(':')[1].split('@')[0] }}"
      caller_number5: "{{ caller_raw.split(\":\")[1].split(\"@\")[0] }}"
      caller_number6: "{{ caller_raw.split(':')[1].split('@')[0] | tojson }}"
      caller_number7: "'{{ caller_raw.split(':')[1].split('@')[0] }}'"
      caller_number8: "{{ ('X' + caller_raw.split(':')[1].split('@')[0])[1:] }}"
      caller_number9: "{{ (caller_raw.split(':')[1].split('@')[0] | tojson).strip('\"') }}"
      caller_number10: "{{ '' ~ caller_raw.split(':')[1].split('@')[0] }}"
      caller_number11: "'{{ caller_raw.split(':')[1].split('@')[0] }}'"
      caller_number12: "{{ 'x'+caller_raw.split(':')[1].split('@')[0] }}"
      caller_number13: "'{{ '+' ~ caller_raw.split(':')[1].split('@')[0].lstrip('+') }}'"
      caller_number14: "'{{ '' ~ ('+' ~ caller_raw.split(':')[1].split('@')[0].lstrip('+')) }}'"
      caller_number15: >-
        {{ (('+' ~ caller_raw.split(':')[1].split('@')[0].lstrip('+')) |
        tojson).strip('"') }}


Resulting in:

caller_raw: '"dongle0" <sip:[email protected]>'
parsed_caller: 31612345678
caller_number: ​​​​+31612345678
caller_number1: 31612345678
caller_number2: '"+31612345678"'
caller_number3: 31612345678
caller_number4: 31612345678
caller_number5: 31612345678
caller_number6: '"+31612345678"'
caller_number7: '''+31612345678'''
caller_number8: 31612345678
caller_number9: 31612345678
caller_number10: 31612345678
caller_number11: '''+31612345678'''
caller_number12: x+31612345678
caller_number13: '''+31612345678'''
caller_number14: '''+31612345678'''
caller_number15: 31612345678

Note that the first try (‘caller_number’) comes very close but due to the zero-width whitespace it is still not matched to the right string (this solution came from someone having the same issue here).

Is there no way to create a numbers string starting with a ‘+’ character?

Some more info

Note that parsed_caller and internal_id in the webhook of HA-SIP contains the exact string I need:

json:
    event: call_disconnected
    caller: '"dongle0" <sip:[email protected]>'
    parsed_caller: '+31612345678'
    internal_id: '+31612345678'

However, passing it to the ‘pick-up’ using:

addon: c6222bff_ha-sip
input:
  command: answer
  number: "{{ trigger.json.parsed_caller }}"

Still results in the Error:

21:17:44.239165 [ ] Got "answer" command for 31612345678
| 21:17:44.239497 [ ] Warning: call not in progress: 31612345678
| 21:17:44.239528 [ ] Currently registered calls:
| 21:17:44.239561 [ ]     +31612345678

When I put those same values inside of a variable, the ‘+’ seems to always be lost (just like mentioned above), making me believe that this is an ‘issue’ with home-assistant and not an issue with the HA-SIP addon.

Hi: Please post the code for your automation (use the </> Preformatted Text option in the editor to preserve indentations).

Is parsed_caller always going to have a country code?

What about caller_raw: will it always be in that format, and with a country code preceded by +?

Hi Dominic! Thanks for your quick response. I can probably parse out the ‘+’ in my sip server (running under asterisk) before passing it on but I would prefer to keep the number as is to also keep it compatible with calling back etc. etc. It works fine if the number does not have a ‘+’ (e.g. non-external calls).

I wasn’t sure what you meant about the problem with caller_number and zero-width whitespace. But would something like this work:

{% set matches = trigger.json.caller | regex_findall("\+\d+") %}
{% if matches %}
  {{ '\u200b' ~ matches[0] }}
{% else %}
  No match
{% endif %}

Many system accept 00 in the beginning as +, so try that.
I can’t say it works for sure, but it is worth a try.

1 Like

Unfortunately I need to pass the number I want to “pick up” to HA-SIP. The matching of the passed number to the call that needs to be picked up is done internally and I can’t do it myself.

Error I get from HA-SIP

How it’s passed to HA-SIP

addon: c6222bff_ha-sip
input:
  command: answer
  number: {{ caller_number }}

Error I get back:

21:17:44.239165 [ ] Got "answer" command for 31612345678
| 21:17:44.239497 [ ] Warning: call not in progress: 31612345678
| 21:17:44.239528 [ ] Currently registered calls:
| 21:17:44.239561 [ ]     +31612345678

Unfortunately this doesn’t work either. I think HA-SIP just matches to the the string starting with “+”. I also tried creating a text-input but this doesn’t seem to work either…

  - action: input_text.set_value
    metadata: {}
    data:
      value: "{{ 'x'+caller_raw.split(':')[1].split('@')[0] }}"
    target:
      entity_id: input_text.incoming_call_number_string_sip1

Results in x+31612345678
If I then do:

 - action: input_text.set_value
    metadata: {}
    data:
      value: "{{ states('input_text.incoming_call_number_string_sip1')[1:] }}"
    target:
      entity_id: input_text.incoming_call_number_string_sip1

I get 31612345678again…

Note

If I then put the ‘x’ back again the ‘+’ is lost…

  • action: input_text.set_value
    metadata: {}
    data:
    value: “{{ ‘x’+states(‘input_text.incoming_call_number_string_sip1’)}}”
    target:
    entity_id: input_text.incoming_call_number_string_sip1

Try adding the + using its code number when you send it to SIP, rather than when you define the variable.

There does seem to be some weirdness with how HA/Jinja treat leading + characters in strings.

Would this be an option?

{{ '"+123"' }}

Otherwise, this hack works in the template editor, because it forces a string somehow (it’s actually a less explicit version of the above).

{{ "+123" | to_json }}

Sending the ‘+’ using unicode didn’t work. I did try the to_json trick in caller_number6 but this resulted in:

caller_number6: '"+31612345678"'

The leading/trailing "in the string probably still causes issues.
I also tried passing this directly to the add-on:

addon: c7744bff_ha-sip
input:
  command: answer
  number: "{{ '\"+31612345678\"' }}"

But this still resulted in:

| 22:47:27.727317 [ ] Got "answer" command for "+31612345678"
| 22:47:27.727506 [ ] Warning: call not in progress: "+31612345678"
| 22:47:27.727538 [ ] Currently registered calls:
| 22:47:27.727569 [ ]     +31612345678

(suggesting the difference here is the leading/trailing ")

I think this is being done by Home Assistant, not by Jinja. It looks like Home Assistant is automatically converting a string it gets from a Jinja {{ }} print statement into a number if the string “looks” like a number. I can’t find a way to suppress this.

It’s possible someone else will be able to figure out a trick for this.

Some other ideas in the meanwhile:

Any chance that SIP tolerates some whitespace, for example, between digits?

Can you configure SIP to drop the country code, or ideally to drop the +?

(I don’t know how much poking around you already did, so sorry if these seem obvious and you’ve already tried).

Assuming this is a limitation of HA, you might be stuck with either sending only an integer or only something that HA concludes is not an integer.

Maybe something along the lines of this

# Call Service: hassio.addon_stdin
- service: hassio.addon_stdin
  data: >
    {{ {
      'addon': 'c6222bff_ha-sip',
      'input': {
        'command': 'answer',
        # keep exactly one leading '+'
        'number': '+' ~ ((trigger.json.parsed_caller | string) | regex_replace('^\\++',''))
      }
    } }}
- service: input_text.set_value
  target:
    entity_id: input_text.incoming_call_number
  data: >
    {{ {
      'value': '+' ~ ((trigger.json.parsed_caller | string) | regex_replace('^\\++',''))
    } }}

Swap trigger.json.parsed_caller for whatever the source is

2 Likes

Brilliant. This should work.

1 Like

The suggestion provided by @zodyking should indeed work.

The problem you’re facing is that normally a jinja template will always result in a string. To make the results more usable, the template parser of Home Assistant converts the result of a template to a native type (so e.g. an integer, floating point number, list, etc).

The side effect of this is that everything which resembles a number, will be converted to a number, but the formatting of that number gets lost. So a + at the start of the number, but also trailing zeros in a value like 12.30 (for example if you want it to be a monetary value)

To avoid this, you can make the value part of a more complex type, like a list or a mapping, and that’s exactly what @zodyking does above.
Although it seems to be a bit complex, this should also work, as the source will be a string including the +:

- action: input_text.set_value
  target:
    entity_id: input_text.incoming_call_number
  data: >
    {{ {
      'value': trigger.json.parsed_caller
    } }}

or:

- action: input_text.set_value
  target:
    entity_id: input_text.incoming_call_number
  data: "{{ dict(value=trigger.json.parsed_caller) }}"
4 Likes

Great explanation.

And of course, one cannot make an exception for +, since mathematically it must be permissible to start a number with that (and hence also - for negative numbers).

Off-topic: I wonder why + was used for the international MSISDN format. Seems like a particularly bad choice when it comes to computers.

The solution @zodyking and @TheFes provided works! Thanks for your help!

@TheFes I do find it interesting that the type is always parsed, even when setting a ‘string’ helper in home assistant to some Jinja template.

1 Like

The result of the template is parsed, and the result is always a string. There is no context anymore at that point if it is a string because a string filter was used, or because a result of template is simply always a string.