Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Outgoing call #19

Merged
merged 20 commits into from
Sep 6, 2024
Merged

Outgoing call #19

merged 20 commits into from
Sep 6, 2024

Conversation

jaminh
Copy link
Contributor

@jaminh jaminh commented Jul 14, 2024

I got as far as successfully calling from the voip library to one of my IP phones through asterisk and playing a pre-recorded message. It is still in a pretty rough state but I thought it might be worthwhile to start getting feedback at this point. I plan to get a Grandstream soon and try testing with that.

jaminh and others added 4 commits July 13, 2024 21:21
It appears the From header is actually more meaningingful for calls
coming from Asterisk, as the Contact header does not distinguish between
which device initiated a call like the From header does.
@jaminh jaminh force-pushed the outgoing-call branch 2 times, most recently from aff1c9e to 70c3b9c Compare July 15, 2024 02:02
voip_utils/call_phone.py Outdated Show resolved Hide resolved
voip_utils/sip.py Outdated Show resolved Hide resolved
voip_utils/sip.py Outdated Show resolved Hide resolved
voip_utils/sip.py Outdated Show resolved Hide resolved
voip_utils/sip.py Outdated Show resolved Hide resolved
voip_utils/sip.py Outdated Show resolved Hide resolved
@jaminh jaminh force-pushed the outgoing-call branch 3 times, most recently from 14c4a00 to 7a774b6 Compare July 16, 2024 01:31
This provides a test script for outgoing calls based on the work
originally started by Michael Hansen. Instructions for running the
outgoing call test script can be found in the README.md file. I tried to
make the acceptable coding for the OPUS codecs in the SIP messages more
flexible, but the number may need to be changed back from 96 to 123 to
work with Grandstream phones. I don't have a Grandstream yet to test
with.
@balloob
Copy link
Contributor

balloob commented Jul 16, 2024

Please don't use force-push, it makes it harder to track fixes

voip_utils/call_phone.py Outdated Show resolved Hide resolved
Modify codec negotiation to work with Grandstream
Move example code out of call_phone.py
@jaminh
Copy link
Contributor Author

jaminh commented Jul 23, 2024

I got a Grandstream this weekend and was able to get calls to it working. I also tried to move the example code out to a separate file.



@dataclass
class SipEndpoint:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think after adding the URI as the device identifier it will probably make more sense to construct this with the URI and then parse out the individual parts rather than building the URI from parts like it is doing now.

@jaminh jaminh requested a review from balloob July 23, 2024 02:07
Comment on lines 417 to 418
print((invite_bytes + sdp_bytes).decode())

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug print still left in the code. If you want to keep this, consider logger. Also you can consider logging sdp_text and invite_text directly to avoid a decoding step.

call_example.py Outdated
lambda call_info, rtcp_state: PreRecordMessageProtocol(
"problem.pcm",
call_info.opus_payload_type,
loop=loop,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's just an example, but let's not pass in the loop. It is not needed in modern asyncio and loop access should rely on get_running_loop

@synesthesiam synesthesiam self-assigned this Aug 1, 2024
source: SipEndpoint,
dest: SipEndpoint,
rtp_port: int,
loop: Optional[asyncio.AbstractEventLoop] = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not pass in the loop

if port:
uri += f":{port}"
if description:
uri = f'"{description} <{uri}>'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only has an opening ", not a closing one. Is that correct?

self._source_endpoint = source
self._dest_endpoint = dest
self._rtp_port = rtp_port
self._request_uri = f"sip:{dest.username}@{dest.host}:{dest.port}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use the get_sip_endpoint helper function here?


for i, line in enumerate(response_lines):
line = line.strip()
if i == 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make this code more readable by turning this around.

if not line:
    break
if i > 0:
    continue

elif not line:
break

if is_ok:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's turn this into a guard clause too:

if not is_ok:
    return


if is_ok:
_LOGGER.debug("Got OK message")
if self.transport is not None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also be a guard clause.

opus_payload_type=opus_payload_type, # Should probably update this to eventually support more codecs
)
)
except Exception:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this? Can we specify which exceptions we expect ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably out of scope for this exercise but the SipDatagramProtocol datagram_received method was also using a try catch, which is why I did the same here. I would suspect we probably want to remove the try/except there as well, and also change the places it was raising ValueError to make it consistent with this new class.

Prefer using guard clauses
Use SipEndpoint attributes where appropriate
Fix end quote for description
# The From header should give us the URI used for sending SIP messages to the device
if headers.get("from") is not None:
caller_uri, caller_name = self._parse_uri_header(headers.get("from"))
caller_endpoint = SipEndpoint(headers.get("from") or "")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did the "or" to make mypy happy, is there a better way to do this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pass "" as a second parameter.

Suggested change
caller_endpoint = SipEndpoint(headers.get("from") or "")
caller_endpoint = SipEndpoint(headers.get("from", ""))

voip_utils/sip.py Outdated Show resolved Hide resolved
Copy link
Contributor

@balloob balloob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I will be home on Sunday and able to test it. (Unless Mike beats me to it).

We're currently adding a new entity type assist-satellie to Home Assistant. This will allow us to target the device to trigger Assist Pipelines (and so we can do calls 😄 )

@balloob
Copy link
Contributor

balloob commented Sep 6, 2024

Tested it and it works! 🎉

@balloob balloob linked an issue Sep 6, 2024 that may be closed by this pull request
@balloob balloob merged commit c32b8f0 into home-assistant-libs:master Sep 6, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bounty: add support for outbound calls
3 participants