-
×InformationNeed Windows 11 help?Check documents on compatibility, FAQs, upgrade information and available fixes.
Windows 11 Support Center.
-
×InformationNeed Windows 11 help?Check documents on compatibility, FAQs, upgrade information and available fixes.
Windows 11 Support Center.
- HP Community
- HP Support Community Knowledge Base
- Poly Phones Knowledge Base
- Poly Phones Knowledge Base
- [FAQ] How can I test the Poly Group Paging using a PC to Dem...
Most Compatible Poly Phones have the ability to either receive Group Pages or Send and Receive Push-To-Talk as described below in Detail
[FAQ] How can I use PTT / Push To Talk / Paging / Page ? - HP Support Community - 9696186
The Group Paging Feature can be useful to simply send a message to one or groups of phones or all phones.
To demonstrate this we created the below article. Simply think of a use case like an announcement in a store that the store closes or to alert everyone in a building about an incident no matter if they are currently speaking to someone else on the phone.
NOTE: You may need to configure these settings as per above FAQ Guide.
This article outlines the steps required to set up Python and run the Poly Group Paging Multicast Simulator on a fresh Windows machine without relying on a pre-compiled .exe file.
The below script and 2x demo wav files are attached >here<
Disclaimer & Support Notice
This script and tool are provided "as-is" strictly for educational, testing, and demonstration purposes. They do not constitute an official, production-ready software product supported by HP or Poly.
Because enterprise network environments, VLAN configurations, and switch vendor implementations vary widely, you may need to tailor or troubleshoot the code for your specific setup. If you require customizations, deeper integrations, or assistance debugging network-specific multicast behaviors, we recommend collaborating with a professional network engineer or utilizing an AI assistant (like ChatGPT/Gemini) to adapt and modify the script to fit your environment's exact requirements.
Prerequisites
-
A Windows PC connected to the same local network (VLAN/subnet) as your HP Poly desk phones.
-
An audio file (
.wav) for testing announcements.
Step 1: Install Python
Since this application is written in Python, the target machine needs the Python runtime environment installed.
-
Go to the official Python Downloads page.
-
Download the latest stable installer for Windows (e.g., Python 3.x).
-
Run the installer.
-
CRITICAL: At the very bottom of the installer window, check the box that says "Add python.exe to PATH".
This ensures you can run Python commands from any terminal window. -
Click Install Now and complete the setup.
Step 2: Install Required Dependencies
The application relies on a standard extension library to handle wideband G.722 audio encoding.
-
Open your Windows Command Prompt (
cmd) or PowerShell. -
Install the G.722 encoding library by running the following command:
Bashpip install G722(Note: All other required modules—such as
tkinter,socket,struct,threading, andwave—come built-in with standard Python, so no extra installs are needed for them).
Step 3: Create the Application Script
-
Open a text editor (such as Notepad, Notepad++, or VS Code).
-
Copy the code below into the editor.
import socket import struct import threading import time import tkinter as tk from tkinter import filedialog, messagebox, ttk import webbrowser import wave # Try importing the pip-installed G722 encoder module try: from G722 import G722 G722_AVAILABLE = True except ImportError: G722_AVAILABLE = False # Default Configuration Constants for Group Paging DEFAULT_IP = "224.0.1.116" DEFAULT_PORT = 5001 class GroupPagingApp: def __init__(self, root): self.root = root self.root.title("HP Poly Group Paging Demo Tool") self.root.geometry("490x520") self.root.resizable(False, False) self.audio_path = "" self.is_transmitting = False self.stop_event = threading.Event() self.payload_size = 160 # Default hidden background payload size self.create_widgets() def create_widgets(self): main_frame = ttk.Frame(self.root, padding="15") main_frame.pack(fill=tk.BOTH, expand=True) # Header frame for Title and Info Button side-by-side header_frame = ttk.Frame(main_frame) header_frame.grid(row=0, column=0, columnspan=2, sticky="ew", pady=(0, 15)) header_frame.columnconfigure(0, weight=1) title_lbl = ttk.Label( header_frame, text="Poly Group Paging Simulator", font=("Arial", 14, "bold"), ) title_lbl.grid(row=0, column=0, sticky="w") info_btn = tk.Button( header_frame, text=" ? Info ", bg="#e0e0e0", fg="#333333", font=("Arial", 9, "bold"), command=self.show_info_window, ) info_btn.grid(row=0, column=1, sticky="e") # 1. Channel Selection (26 to 50 for Group Paging) ttk.Label(main_frame, text="Page Group (26-50):").grid( row=1, column=0, sticky=tk.W, pady=5 ) self.chan_cb = ttk.Combobox( main_frame, values=[str(i) for i in range(26, 51)], width=12, state="readonly" ) self.chan_cb.grid(row=1, column=1, sticky=tk.W, pady=5) self.chan_cb.set("26") info_lbl = ttk.Label( main_frame, text="(Ch 49: Priority | Ch 50: Emergency)", font=("Arial", 8, "italic"), foreground="gray", ) info_lbl.grid(row=2, column=1, sticky=tk.W, pady=(0, 5)) # 2. Port Configuration (0 to 65535) ttk.Label(main_frame, text="Destination Port (0-65535):").grid( row=3, column=0, sticky=tk.W, pady=5 ) self.port_ent = ttk.Entry(main_frame, width=14) self.port_ent.grid(row=3, column=1, sticky=tk.W, pady=5) self.port_ent.insert(0, str(DEFAULT_PORT)) # 3. Codec Selection (Automatically manages background payload size) ttk.Label(main_frame, text="Audio Codec:").grid( row=4, column=0, sticky=tk.W, pady=5 ) self.codec_cb = ttk.Combobox( main_frame, values=["G.711mu", "G.722", "G.726QI"], width=12, state="readonly", ) self.codec_cb.grid(row=4, column=1, sticky=tk.W, pady=5) self.codec_cb.set("G.722" if G722_AVAILABLE else "G.711mu") self.codec_cb.bind("<<ComboboxSelected>>", self.auto_set_payload) self.auto_set_payload() # 4. Page Mode Payload Size (ms) ttk.Label(main_frame, text="Page Mode Payload (ms):").grid( row=5, column=0, sticky=tk.W, pady=5 ) self.pagemode_cb = ttk.Combobox( main_frame, values=[str(i) for i in range(10, 81, 10)], width=12, state="readonly", ) self.pagemode_cb.grid(row=5, column=1, sticky=tk.W, pady=5) self.pagemode_cb.set("20") # 5. Display Name / Caller ID (up to 64 octet UTF-8) ttk.Label(main_frame, text="Display Name (UTF-8):").grid( row=6, column=0, sticky=tk.W, pady=5 ) self.name_ent = ttk.Entry(main_frame, width=28) self.name_ent.grid(row=6, column=1, sticky=tk.W, pady=5) self.name_ent.insert(0, "Page Demo") # 6. Audio File Selection (.wav) ttk.Label(main_frame, text="Audio File (.wav):").grid( row=7, column=0, sticky=tk.W, pady=5 ) file_frame = ttk.Frame(main_frame) file_frame.grid(row=7, column=1, sticky=tk.W, pady=5) self.file_lbl = ttk.Label(file_frame, text="No file selected", width=16) self.file_lbl.pack(side=tk.LEFT, padx=(0, 5)) browse_btn = ttk.Button(file_frame, text="Browse", command=self.select_file) browse_btn.pack(side=tk.LEFT) # 7. TEST / Stop Button self.test_btn = tk.Button( main_frame, text="TEST (Send Page)", bg="#d9534f", fg="white", font=("Arial", 11, "bold"), height=2, command=self.handle_button_press, ) self.test_btn.grid( row=8, column=0, columnspan=2, sticky="ew", pady=(20, 5) ) # Status readout self.status_lbl = ttk.Label( main_frame, text="Status: Ready", font=("Arial", 9, "italic"), foreground="blue", ) self.status_lbl.grid(row=9, column=0, columnspan=2, sticky=tk.W, pady=5) def show_info_window(self): info_win = tk.Toplevel(self.root) info_win.title("About - HP Poly Group Paging Demo") info_win.geometry("450x320") info_win.resizable(False, False) frame = ttk.Frame(info_win, padding="15") frame.pack(fill=tk.BOTH, expand=True) ttk.Label( frame, text="HP Poly Group Paging Demo Tool", font=("Arial", 11, "bold") ).pack(pady=(0, 5)) ttk.Label(frame, text="Version 1.0 | Released: 2026", font=("Arial", 9)).pack( pady=(0, 10) ) desc_text = ( "This tool simulates one-way UDP Multicast Group Paging\n" "announcements for HP Poly desk phones (Channels 26-50)\n" "based on Engineering Advisory 70568 packet formats.\n\n" "Supported Codecs: G.711mu, G.722, and G.726QI." ) ttk.Label(frame, text=desc_text, justify=tk.CENTER).pack(pady=(0, 15)) ttk.Label( frame, text="For complete documentation & configuration guides:", font=("Arial", 9, "italic") ).pack() link_lbl = tk.Label( frame, text="Visit HP Community PTT/Paging FAQ", fg="blue", cursor="hand2", font=("Arial", 9, "underline"), ) link_lbl.pack(pady=5) link_lbl.bind( "<Button-1>", lambda e: webbrowser.open( "https://h30434.www3.hp.com/t5/Desk-and-IP-Conference-Phones/FAQ-How-can-I-use-PTT-Push-To-Talk-Paging-Page/td-p/9696186" ), ) close_btn = ttk.Button( frame, text="Close", command=info_win.destroy ) close_btn.pack(pady=(15, 0)) def auto_set_payload(self, event=None): selected_codec = self.codec_cb.get() if selected_codec in ["G.711mu", "G.722"]: self.payload_size = 160 elif selected_codec == "G.726QI": self.payload_size = 80 def select_file(self): filename = filedialog.askopenfilename( title="Select Audio File", filetypes=[("WAV files", "*.wav")] ) if filename: self.audio_path = filename self.file_lbl.config(text=filename.split("/")[-1]) def build_header(self, opcode, channel, caller_id): cid_encoded = caller_id.encode("utf-8")[:13].ljust(13, b"\x00") host_serial = 0x12345678 return struct.pack(">BBIB13s", opcode, channel, host_serial, 13, cid_encoded) def linear_to_ulaw(self, sample): BIAS = 133 CLIP = 32635 sign = 0 if sample < 0: sample = -sample sign = 0x80 if sample > CLIP: sample = CLIP sample += BIAS exponent = 7 exp_mask = 0x4000 while (sample & exp_mask) == 0 and exponent > 0: exponent -= 1 exp_mask >>= 1 mantissa = (sample >> (exponent + 3)) & 0x0F return (~(sign | (exponent << 4) | mantissa)) & 0xFF def process_wav_file(self, file_path, chunk_size, codec_choice): with wave.open(file_path, "rb") as wf: n_channels = wf.getnchannels() sampwidth = wf.getsampwidth() framerate = wf.getframerate() raw_data = wf.readframes(wf.getnframes()) if sampwidth == 2: samples = struct.unpack(f"<{len(raw_data)//2}h", raw_data) elif sampwidth == 1: samples = [(b - 128) * 256 for b in raw_data] else: samples = [0] * (len(raw_data) // sampwidth) if n_channels == 2: samples = [ int((samples[i] + samples[i + 1]) / 2) for i in range(0, len(samples), 2) ] target_rate = 16000 if codec_choice == "G.722" else 8000 if framerate != target_rate: ratio = framerate / float(target_rate) samples = [ samples[int(i * ratio)] for i in range(int(len(samples) / ratio)) ] if codec_choice == "G.722": if not G722_AVAILABLE: raise Exception("G722 package is not installed. Run 'pip install G722'.") encoder = G722(16000, 64000) frame_size = 320 encoded_bytes = bytearray() for i in range(0, len(samples), frame_size): chunk_samples = samples[i : i + frame_size] if len(chunk_samples) < frame_size: chunk_samples = list(chunk_samples) + [0] * ( frame_size - len(chunk_samples) ) encoded_bytes.extend(encoder.encode(chunk_samples)) encoded_bytes = bytes(encoded_bytes) else: encoded_bytes = bytes([self.linear_to_ulaw(s) for s in samples]) return [ encoded_bytes[i : i + chunk_size].ljust( chunk_size, b"\x7f" if codec_choice == "G.711mu" else b"\x55" ) for i in range(0, len(encoded_bytes), chunk_size) ] def handle_button_press(self): if not self.is_transmitting: if not self.audio_path: messagebox.showwarning( "Missing Audio File", "Please select a valid .wav audio file before sending the page.", ) return self.stop_event.clear() threading.Thread(target=self.send_page_sequence, daemon=True).start() else: self.stop_event.set() self.status_lbl.config(text="Status: Stopping page...") def send_page_sequence(self): self.is_transmitting = True self.test_btn.config(bg="#333333", text="STOP PAGE") try: channel = int(self.chan_cb.get()) port = int(self.port_ent.get()) caller_id = self.name_ent.get() payload_size = self.payload_size codec_choice = self.codec_cb.get() codec_map = {"G.711mu": 0x00, "G.722": 0x09, "G.726QI": 0xFD} codec_id = codec_map.get(codec_choice, 0x00) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) # 1. Send 31 Page Alert Packets (0x0F) self.status_lbl.config(text="Status: Sending Page Alert Packets...") alert_packet = self.build_header(0x0F, channel, caller_id) for _ in range(31): if self.stop_event.is_set(): break sock.sendto(alert_packet, (DEFAULT_IP, port)) time.sleep(0.03) time.sleep(0.05) # 2. Transmit Audio Payload Packets (0x10) with 20ms pacing if not self.stop_event.is_set(): self.status_lbl.config( text=f"Status: Broadcasting Page Audio ({codec_choice})..." ) audio_chunks = self.process_wav_file( self.audio_path, payload_size, codec_choice ) timestamp = 0 prev_chunk = b"\x55" * payload_size for chunk in audio_chunks: if self.stop_event.is_set(): break audio_header = struct.pack(">BBI", codec_id, 0x00, timestamp) audio_payload = audio_header + prev_chunk + chunk packet = self.build_header(0x10, channel, caller_id) + audio_payload sock.sendto(packet, (DEFAULT_IP, port)) prev_chunk = chunk timestamp += 320 if codec_choice == "G.722" else 160 time.sleep(0.02) # Strict 20ms timing time.sleep(0.2) # 3. Send Full 12 End Packets (0xFF) if not self.stop_event.is_set(): self.status_lbl.config(text="Status: Closing paging session...") end_packet = self.build_header(0xFF, channel, caller_id) for _ in range(12): sock.sendto(end_packet, (DEFAULT_IP, port)) time.sleep(0.03) self.status_lbl.config(text="Status: Page Broadcast Finished.") except Exception as e: messagebox.showerror("Error", str(e)) self.status_lbl.config(text="Status: Error encountered.") finally: self.is_transmitting = False self.test_btn.config(bg="#d9534f", text="TEST (Send Page)") if __name__ == "__main__": root = tk.Tk() app = GroupPagingApp(root) root.mainloop() -
Save the file as
poly_ptt_demo.pyin an easily accessible folder (e.g., your Desktop or a dedicatedPTT_Demofolder).
Step 4: Prepare Your Test Audio File
To ensure your announcements play clearly on the Poly desk phone without errors:
-
Open a free audio editor like Audacity.
-
Record or import your announcement (e.g., "HP Poly PTT Demo").
-
Mix the audio track down to Mono.
-
Set the Project Sample Rate:
-
Choose 8000 Hz if you plan to use the G.711mu codec.
-
Choose 16000 Hz if you plan to use the G.722 codec.
-
-
Go to File > Export Audio, select WAV (Microsoft), and choose Signed 16-bit PCM encoding. Save the file.
NOTE: A PolyDemoSpeech_G711mu.wav & PolyDemoSpeech_G722mu.wav are attached within the poly_ptt_demo.zip file
Step 5: Run the Application
-
Open your Command Prompt or PowerShell and navigate to the folder where you saved your script:
cd C:\Users\YourUsername\Desktop\PTT_Demo -
Launch the application by running:
python poly_ptt_demo.py - On a compatible Poly Phone, enable Group Paging
SteffenBaierUK_2-1787147890895.png
-
The graphical user interface (GUI) will open instantly!
SteffenBaierUK_0-1787147573552.png
-
Select your target channel (26-50), choose your codec, browse for your prepared
.wavfile, and click TEST (Send page) to stream live multicast packets directly to your compatible Poly desk phones.
Step 6: Create an Exe Application
Step 1: Install PyInstaller
PyInstaller is the industry-standard tool for packaging Python scripts into standalone binaries.
-
Open your Windows Command Prompt (
cmd) or PowerShell. -
Run the following command to install PyInstaller:
Bashpip install pyinstaller
Step 2: Compile the Script into an Executable
Because your script relies on a third-party pip package (G722) for wideband audio encoding, you must instruct PyInstaller to collect its modules and binaries along with your script.
-
Navigate in your terminal to the folder where your script (
group_paging_demo.py) is saved:cd C:\Users\YourUsername\Desktop\Paging_Demo -
Run the build command:
pyinstaller --onefile --noconsole --collect-all G722 group_paging_demo.py
What these flags mean:
-
--onefile: Bundles everything into a single, clean.exefile rather than a messy folder of dependencies. -
--noconsole: Hides the background command prompt window, ensuring only your clean graphical user interface (GUI) appears when launched. -
--collect-all G722: Ensures PyInstaller bundles all internal binaries, submodules, and data files from theG722package so wideband audio encoding works seamlessly on target machines.
Step 3: Locate Your Portable Executable
-
Once the build process finishes (it usually takes 15–30 seconds and outputs a
Completed successfullymessage), check your project folder. -
PyInstaller will have created a few temporary folders (
build,dist) and a.specfile. -
Open the
distfolder. Inside, you will find your standalonegroup_paging_demo.exefile!
Step 4: Share and Test
-
You can now copy that single
.exefile onto a USB drive, attach it to an email, or share it on a network share. -
Drag it to any fresh Windows PC, double-click it, and the graphical tool will launch immediately—no Python setup required.
(Note: Just remember that the end user still needs to bring their own prepared .wav audio files and ensure their computer is on the same local network subnet as the Poly desk phones).
Network FAQ: Routing Multicast Paging Across Multiple Switches
By default, broadcast and multicast traffic stays confined to a single local network segment (VLAN). When your HP Poly desk phones and the computer running the paging tool are on different switches, subnets, or VLANs, specific network configurations are required to let the multicast stream traverse your enterprise infrastructure.
1. IGMP Snooping
-
What it does:
Prevents multicast traffic from flooding every single port on your switches like a broadcast. -
What to look at:
Ensure IGMP Snooping is enabled on all intermediate network switches. This ensures switches intelligently forward the multicast stream (224.0.1.116) only to the switch ports where Poly phones are actively listening. -
IGMP Querier:
At least one Layer 3 switch or router on the network must be configured as the IGMP Querier to maintain active group memberships.
2. Inter-VLAN Multicast Routing (PIM)
-
What it does: If your paging source computer is on one VLAN (e.g., Data VLAN 10) and your Poly desk phones are on another VLAN (e.g., Voice VLAN 20), the stream must cross a Layer 3 boundary.
-
What to look at: Your Core Switch or Router must have Multicast Routing enabled, specifically PIM (Protocol Independent Multicast)—either PIM-Dense Mode (easier for small deployments) or PIM-Sparse Mode (recommended for enterprise networks). Without PIM, Layer 3 devices will drop the multicast packets at the subnet boundary.
3. Multicast TTL (Time-To-Live)
-
What it does: Controls how many "router hops" a multicast packet can cross before it is discarded.
-
What to look at: The script sets the multicast TTL to
2(sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)). If your paging packets have to cross more than two router hops/subnets to reach distant switches, you may need to increase the TTL value in the Python script.