Auto-detect local IP address for pyVoIP binding (v2.1.1)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
name: "SIP Voice Notifier"
|
name: "SIP Voice Notifier"
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
slug: "sip-notifier"
|
slug: "sip-notifier"
|
||||||
description: "Send voice notifications via SIP phone calls"
|
description: "Send voice notifications via SIP phone calls"
|
||||||
arch:
|
arch:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import socket
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
@@ -26,6 +27,21 @@ CONFIG = {}
|
|||||||
DEFAULT_SAMPLE_RATE = 8000
|
DEFAULT_SAMPLE_RATE = 8000
|
||||||
|
|
||||||
|
|
||||||
|
def get_local_ip():
|
||||||
|
"""Get local IP address of the container."""
|
||||||
|
try:
|
||||||
|
# Create a socket to determine the local IP
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
# Connect to an external address (doesn't actually send data)
|
||||||
|
s.connect(("8.8.8.8", 80))
|
||||||
|
local_ip = s.getsockname()[0]
|
||||||
|
s.close()
|
||||||
|
return local_ip
|
||||||
|
except Exception as e:
|
||||||
|
_LOGGER.warning(f"Could not auto-detect IP: {e}, using 0.0.0.0")
|
||||||
|
return "0.0.0.0"
|
||||||
|
|
||||||
|
|
||||||
def download_and_convert_audio(url: str) -> str:
|
def download_and_convert_audio(url: str) -> str:
|
||||||
"""Download and convert audio to WAV."""
|
"""Download and convert audio to WAV."""
|
||||||
parsed_url = urlparse(url)
|
parsed_url = urlparse(url)
|
||||||
@@ -98,7 +114,12 @@ def place_sip_call(destination: str, audio_file: str, duration: int):
|
|||||||
sip_server = CONFIG['sip_server']
|
sip_server = CONFIG['sip_server']
|
||||||
sip_password = CONFIG['sip_password']
|
sip_password = CONFIG['sip_password']
|
||||||
|
|
||||||
_LOGGER.info(f"Connecting to SIP server: {sip_server}")
|
# Get local IP
|
||||||
|
local_ip = get_local_ip()
|
||||||
|
|
||||||
|
_LOGGER.info(f"Local IP: {local_ip}")
|
||||||
|
_LOGGER.info(f"SIP Server: {sip_server}")
|
||||||
|
_LOGGER.info(f"Calling: {destination}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Create VoIP phone
|
# Create VoIP phone
|
||||||
@@ -108,48 +129,54 @@ def place_sip_call(destination: str, audio_file: str, duration: int):
|
|||||||
sip_user,
|
sip_user,
|
||||||
sip_password,
|
sip_password,
|
||||||
callCallback=None,
|
callCallback=None,
|
||||||
myIP=None,
|
myIP=local_ip,
|
||||||
sipPort=5060,
|
sipPort=5060,
|
||||||
rtpPortLow=10000,
|
rtpPortLow=10000,
|
||||||
rtpPortHigh=20000
|
rtpPortHigh=20000
|
||||||
)
|
)
|
||||||
|
|
||||||
phone.start()
|
phone.start()
|
||||||
time.sleep(2) # Wait for registration
|
_LOGGER.info("SIP phone started, waiting for registration...")
|
||||||
|
time.sleep(3) # Wait for registration
|
||||||
|
|
||||||
_LOGGER.info(f"Calling: {destination}")
|
_LOGGER.info(f"Making call to: {destination}")
|
||||||
|
|
||||||
# Make call
|
# Make call
|
||||||
call = phone.call(destination)
|
call = phone.call(destination)
|
||||||
|
|
||||||
# Wait for call to be answered
|
# Wait for call to be answered
|
||||||
timeout = 10
|
timeout = 15
|
||||||
elapsed = 0
|
elapsed = 0
|
||||||
while call.state != CallState.ANSWERED and elapsed < timeout:
|
while call.state != CallState.ANSWERED and elapsed < timeout:
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
elapsed += 0.5
|
elapsed += 0.5
|
||||||
|
if elapsed % 2 == 0:
|
||||||
|
_LOGGER.info(f"Waiting for answer... ({elapsed}s)")
|
||||||
|
|
||||||
if call.state != CallState.ANSWERED:
|
if call.state != CallState.ANSWERED:
|
||||||
_LOGGER.warning("Call not answered within timeout")
|
_LOGGER.warning(f"Call not answered within {timeout}s timeout")
|
||||||
|
_LOGGER.info(f"Final call state: {call.state}")
|
||||||
call.hangup()
|
call.hangup()
|
||||||
phone.stop()
|
phone.stop()
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.info("Call answered! Playing audio...")
|
_LOGGER.info("✅ Call answered! Playing audio...")
|
||||||
|
|
||||||
# Transmit audio file
|
# Transmit audio file
|
||||||
call.write_audio(audio_file)
|
call.write_audio(audio_file)
|
||||||
|
|
||||||
# Keep call active
|
# Keep call active
|
||||||
_LOGGER.info(f"Call active for {duration}s")
|
_LOGGER.info(f"Keeping call active for {duration}s...")
|
||||||
time.sleep(duration)
|
time.sleep(duration)
|
||||||
|
|
||||||
# Hangup
|
# Hangup
|
||||||
_LOGGER.info("Hanging up...")
|
_LOGGER.info("Hanging up...")
|
||||||
call.hangup()
|
call.hangup()
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
phone.stop()
|
phone.stop()
|
||||||
|
_LOGGER.info("Call completed successfully")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_LOGGER.error(f"Call error: {e}", exc_info=True)
|
_LOGGER.error(f"Call error: {e}", exc_info=True)
|
||||||
@@ -192,10 +219,9 @@ def handle_send_notification():
|
|||||||
temp_files.append(audio_file)
|
temp_files.append(audio_file)
|
||||||
|
|
||||||
# Place call
|
# Place call
|
||||||
_LOGGER.info(f"Calling: {destination}")
|
|
||||||
place_sip_call(destination, audio_file, duration)
|
place_sip_call(destination, audio_file, duration)
|
||||||
|
|
||||||
return jsonify({'status': 'success'}), 200
|
return jsonify({'status': 'success', 'message': 'Call completed'}), 200
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Cleanup
|
# Cleanup
|
||||||
@@ -233,6 +259,7 @@ if __name__ == '__main__':
|
|||||||
_LOGGER.info("Using pyVoIP from git (latest version)")
|
_LOGGER.info("Using pyVoIP from git (latest version)")
|
||||||
_LOGGER.info(f"SIP Server: {CONFIG.get('sip_server', 'not configured')}")
|
_LOGGER.info(f"SIP Server: {CONFIG.get('sip_server', 'not configured')}")
|
||||||
_LOGGER.info(f"SIP User: {CONFIG.get('sip_user', 'not configured')}")
|
_LOGGER.info(f"SIP User: {CONFIG.get('sip_user', 'not configured')}")
|
||||||
|
_LOGGER.info(f"Local IP: {get_local_ip()}")
|
||||||
_LOGGER.info("")
|
_LOGGER.info("")
|
||||||
_LOGGER.info("To use this add-on, add configuration to configuration.yaml")
|
_LOGGER.info("To use this add-on, add configuration to configuration.yaml")
|
||||||
_LOGGER.info("See add-on README for details")
|
_LOGGER.info("See add-on README for details")
|
||||||
|
|||||||
Reference in New Issue
Block a user