• ×
    Information
    Need Windows 11 help?
    Check documents on compatibility, FAQs, upgrade information and available fixes.
    Windows 11 Support Center.
  • ×
    Information
    Need Windows 11 help?
    Check documents on compatibility, FAQs, upgrade information and available fixes.
    Windows 11 Support Center.
Guidelines
Join the HP Community Solve‑a‑thon | Help Others & Share Your Solutions | Live on Zoom | 2:30 PM to 2:30 AM IST | Every Wednesday Click here to know more
SteffenBaierUK
Level 16
Views : 57
No ratings

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.

  1. Go to the official Python Downloads page.

  2. Download the latest stable installer for Windows (e.g., Python 3.x).

  3. Run the installer.

  4. 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.

  5. 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.

  1. Open your Windows Command Prompt (cmd) or PowerShell.

  2. Install the G.722 encoding library by running the following command:

    Bash
     
    pip install G722
    

    (Note: All other required modules—such as tkinter, socket, struct, threading, and wave—come built-in with standard Python, so no extra installs are needed for them).

Step 3: Create the Application Script

  1. Open a text editor (such as Notepad, Notepad++, or VS Code).

  2. 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()

     

  3. Save the file as poly_ptt_demo.py in an easily accessible folder (e.g., your Desktop or a dedicated PTT_Demo folder).

 

Step 4: Prepare Your Test Audio File

To ensure your announcements play clearly on the Poly desk phone without errors:

  1. Open a free audio editor like Audacity.

  2. Record or import your announcement (e.g., "HP Poly PTT Demo").

  3. Mix the audio track down to Mono.

  4. 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.

  5. 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

  1. Open your Command Prompt or PowerShell and navigate to the folder where you saved your script:

     
    cd C:\Users\YourUsername\Desktop\PTT_Demo
    
  2. Launch the application by running:

     
    python poly_ptt_demo.py
    
  3. On a compatible Poly Phone, enable Group Paging

    SteffenBaierUK_2-1787147890895.pngSteffenBaierUK_2-1787147890895.png

     

  4. The graphical user interface (GUI) will open instantly!

    SteffenBaierUK_0-1787147573552.pngSteffenBaierUK_0-1787147573552.png

     

  5. Select your target channel (26-50), choose your codec, browse for your prepared .wav file, 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.

  1. Open your Windows Command Prompt (cmd) or PowerShell.

  2. Run the following command to install PyInstaller:

    Bash
     
    pip 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.

  1. Navigate in your terminal to the folder where your script (group_paging_demo.py) is saved:

    cd C:\Users\YourUsername\Desktop\Paging_Demo
    
  2. 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 .exe file 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 the G722 package so wideband audio encoding works seamlessly on target machines.

Step 3: Locate Your Portable Executable

  1. Once the build process finishes (it usually takes 15–30 seconds and outputs a Completed successfully message), check your project folder.

  2. PyInstaller will have created a few temporary folders (build, dist) and a .spec file.

  3. Open the dist folder. Inside, you will find your standalone group_paging_demo.exe file!

Step 4: Share and Test

  • You can now copy that single .exe file 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.

Contributors
† The opinions expressed above are the personal opinions of the authors, not of HP. By using this site, you accept the <a href="https://www8.hp.com/us/en/terms-of-use.html" class="udrlinesmall">Terms of Use</a> and <a href="/t5/custom/page/page-id/hp.rulespage" class="udrlinesmall"> Rules of Participation</a>.
-->