Bulk Update NEC ITK Phone SIP User and Password

We started our NEC SV9100 deployment planning to have system on 10-46-01 Register Mode Plug and Play mode after deployment, which meant we would not need to save extension on phone.
Half way through we decided we would like to use Manual mode which requires SIP User-ID and Password be saved on phone. As we didn’t want to manually go to each phone to change settings the following script uses the 15-05 export from PcPro and logs into each IP Phone and sets its Extension and Password.

Use carefully as if you get the settings wrong you could disconnect all your phones.

Bulk update NEC ITK Phones saved SIP user-id and Password
Will update the saved UserID, Password and Extension on an NEC ITK series phone with the Ext numbr it is registered to an SV9100 system with
Requries a CSV export from PCPro SV9100 of 15-05. This contains the Extension to IP address mapping.
Assumes the SIP userid and Password is the same for all extensions
Only tested on SV9100 AU and ITK-24CG phone. Strongly suggest testing on a single extension before bulk updating
In SV9100 PCPro -> Reports System data
Tick 15-05 -> Export as CSV
Update phone username and password below and the SIP account password
Author: James Rudd
Blog : http://jrudd.org/
# Phone Login
$User = "ADMIN"
$Password = "<Pass>"
# Export 15-05 Phones List as CSV
$CSVfile = "C:\Temp\15-XX(3).csv"
# Account Pass
$SipPassword = "123456789"
$IpPhoneList = Import-csv $CSVfile -Header Extension,Type,MAC,Nickname,IPAddress |Select-Object -Skip 1
# Log Errors
$phoneErrors = New-Object System.Collections.Generic.List[System.Object]
# Loop over Phones
foreach ($phone in $IpPhoneList){
$SipExt = $phone.Extension
if($SipExt -notmatch "\d{3}"){
Write-Host "Not a valid Extension ${SipExt} " -ForegroundColor Red
$phoneErrors.Add("${SipExt}: Not a valid Extension")
$PhoneIP = $phone.IPAddress
$phoneBase = "http://$PhoneIP"
if($phone.Type -notlike "DT900*"){
Write-Host "Extension ${SipExt} is not a DT Phone $PhoneIP" -ForegroundColor Yellow
$phoneErrors.Add("${SipExt}: Not a DT Phone")
# Test Ping phone to check up
if (-not (Test-Connection -ComputerName $PhoneIP -Quiet)){
Write-Host "Extension ${SipExt} could not ping $PhoneIP" -ForegroundColor Red
$phoneErrors.Add("${SipExt}: Could not ping $PhoneIP")
# Login to Phone (this will lock the phone)
#Referer: http://IP/index.cgi?login=0\r\n
$URL = "${phoneBase}/index.cgi?username=${User}&password=${Password}"
$R = Invoke-WebRequest -Uri $URL -SessionVariable necPhone -Method GET
# Get Session ID
#<frame src="./menu.cgi?session=2c4a" name="left" >
if ($R.Content -match 'index\.cgi\?session=([\d\w]+)'){
} else {
Write-Host "Unable to get session ID for $SipExt on $PhoneIP" -ForegroundColor Red
$phoneErrors.Add("${SipExt}: No Session ID")
# Set UserID
$URL = "${phoneBase}/index.cgi?session=$sessionID&set=40a0418&item=${SipExt}"
$R = Invoke-WebRequest -Uri $URL -WebSession $necPhone
# Set Password
# /index.cgi?session=2c4a&set=40a0583&item=1234
$URL = "${phoneBase}/index.cgi?session=$sessionID&set=40a0583&item=${SipPassword}"
$R = Invoke-WebRequest -Uri $URL -WebSession $necPhone
# Set Extension
# http://IP/index.cgi?session=2c4a&set=40a041a&item=359
$URL = "${phoneBase}/index.cgi?session=$sessionID&set=40a041a&item=${SipExt}"
$R = Invoke-WebRequest -Uri $URL -WebSession $necPhone
# Save and Reboot
$URL = "${phoneBase}/index.cgi?session=$sessionID&set=all"
$R = Invoke-WebRequest -Uri $URL -WebSession $necPhone
Write-Host "Updated Extension $SipExt on $PhoneIP" -ForegroundColor Green
$Errors = $phoneErrors -join "`r`n"
Write-Host "Failed Extensions:`r`n$Errors" -ForegroundColor Red
Posted in PABX | Leave a comment

NEC SV9100 Bulk Photo Upload

On our new SV9100 system we needed a way to bulk upload user thumbnails for their extensions to appear on the new colour screen ITK phones. NEC does not appears to have any way to do this so I have written the following script.

It requires a folder containing your images with the filename ending in the extension it should be uploaded to. This works best if you pre-crop your images to square or similar as on phone the image will be cropped.

Bulk upload extension photos to SV9100
Script to upload photos based on extension number to NEC SV9100 PABX, or perform other bulk individual operations
Photo can be checked at "http://<IP>/UserImages/000.jpg" where number is port – 1
Image names must have extension as last part of name e.g. "Name 122.jpg"
Update stored directory of photos below
Enter the password for a User1 level user.
Author: James Rudd
Blog : http://jrudd.org/
# Folder where images are stored. Image names must have extension as last part of name e.g. "Name 342.jpg"
$TARGET_FOLDER_PATH = "C:\Temp\Pics\2020"
$pabxHost="<PABX IP>"
$pabxBase = "http://$pabxHost" # Allows HTTPS if configured
# Configure Variables
$necSep = [char]18 # NEC Item seperator
$LF = "`r`n"
# Needed to resize images for PABX
Function ResizeImage() {
param([String]$ImagePath, [Int]$Quality = 90, [Int]$targetSize, [String]$OutputLocation)
Add-Type -AssemblyName "System.Drawing"
$img = [System.Drawing.Image]::FromFile($ImagePath)
$CanvasWidth = $targetSize
$CanvasHeight = $targetSize
#Encoder parameter for image quality
$ImageEncoder = [System.Drawing.Imaging.Encoder]::Quality
$encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter($ImageEncoder, $Quality)
# get codec
$Codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object {$_.MimeType -eq 'image/jpeg'}
#compute the final ratio to use
$ratioX = $CanvasWidth / $img.Width;
$ratioY = $CanvasHeight / $img.Height;
$ratio = $ratioY
if ($ratioX -le $ratioY) {
$ratio = $ratioX
$newWidth = [int] ($img.Width * $ratio)
$newHeight = [int] ($img.Height * $ratio)
$bmpResized = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
$graph = [System.Drawing.Graphics]::FromImage($bmpResized)
$graph.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graph.DrawImage($img, 0, 0, $newWidth, $newHeight)
#save to file
$bmpResized.Save($OutputLocation, $Codec, $($encoderParams))
# Get Session ID
$R = Invoke-WebRequest "${pabxBase}/Login.htm" -SessionVariable necLogin -Method POST
# Login.htm?sessionId=&LOGIN()
$UrlLogin = "${pabxBase}/Login.htm%3FsessionId%3D%26LOGIN%28%29"
$Body = @{
formData = "0${necSep}userName=$pabxUser${necSep}language=onChange=${necSep}password=$pabxPass"
$R = Invoke-WebRequest -Uri $UrlLogin -WebSession $necLogin -Method POST -Body $Body
$extPort = @{}
foreach ($script in $R.Scripts) {
# Get Session ID
if ($script.innerHTML -match 'g_sessionId="(\S+)";'){
#Generate extension to port mappings
if ($script.src -like "*.jsx"){
$URL = "${pabxBase}/" + $script.src
$extList = (Invoke-WebRequest $URL -WebSession $necLogin).Content.Split(";")
foreach ($ext in $extList) {
if ( $ext -match 'tel\((\d+),"(\d+)","(\w+)"\)'){
$extPort.Add($Matches[2],@($Matches[1],$Matches[3])) # Ext = (Port, Type [ve|tel])
if (($sessionID -eq "") -or ($extPort.Count -lt 5 )){
#Failed to login and download extensions
Write-Host "Could not login or download extensions list" -ForegroundColor Red
# Go through folder and upload images to extension of filename
$ThumbJpgDir = "$TARGET_FOLDER_PATH\thumb"
New-Item -ItemType Directory -Force -Path $ThumbJpgDir
#for($port=0;$port -lt 2; $port++) {
Get-ChildItem $TARGET_FOLDER_PATH -Filter *.jpg |
Foreach-Object {
$fileName = $_.Name
# Check file name contains extension
if ($fileName -match '(\d+).[jJ][pP][gG]' ){
else {
Write-Host "Could not match $fileName" -ForegroundColor Red
# Check NEC system has a matching Extension
if ($extPort.ContainsKey($ext)){
$portNum=[int]$extPort[$ext][0] + 1
} else {
Write-Host "Could not find $ext on NEC System" -ForegroundColor Red
$filePath = "$ThumbJpgDir\$fileName"
# Image must have longest edge as 160 pixel
# Max file size is 15*1024 bytes = 15kB
ResizeImage $LDA_TARGET_FILE_NAME 90 160 $filePath
# Load Initial Page.
# Best not to load as can lock phone. Not needed for upload
$URLassemb = "?sessionId=${sessionID}&FEAT_TEL%2823,0,${portNum}%29"
#$URL = "${pabxBase}/UserPro.htm" + [uri]::EscapeDataString($URLassemb)
$URL = "${pabxBase}/UserPro.htm" + $URLassemb
# Not sure if I need to load page
$WebResponse = Invoke-WebRequest $URL -WebSession $necLogin
Write-Verbose $URL
# Post Image
$URLassemb = "?sessionId=${sessionID}&UPLOAD_USER_IMG()"
$URLPost = "${pabxBase}/UserPro.htm" + $URLassemb
# From https://stackoverflow.com/questions/61463759/powershell-upload-a-pdf-file-using-boundary-mime-to-rightfax-web-service
$boundary = [System.Guid]::NewGuid().ToString()
$boundary = "—-WebKitFormBoundary" + $boundary.Replace("-","").SubString(0,16)
# Headers
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Accept", "*/*")
$headers.Add("Referer", $URL)
$headers.Add("Origin", "${pabxBase}")
$headers.Add("Accept-Encoding", "gzip, deflate")
$headers.Add("Accept-Language", "en-AU,en-GB;q=0.9,en-US;q=0.8,en;q=0.7")
$headers.Add("dnt", "1")
# Read in Image
$fileRead = [System.IO.File]::ReadAllBytes($FilePath)
$fileEnc = [System.Text.Encoding]::GetEncoding("iso-8859-1").GetString($fileRead)
# Generate the multipart message
$bodyLines = (
"Content-Disposition: form-data; name=`"portNo`"$LF",
"Content-Disposition: form-data; name=`"fileName`"$LF",
"Content-Disposition: form-data; name=`"userImagePath`"; filename=`"newImg.jpg`"",
"Content-Type: image/jpeg$LF",
) -join $LF
$Attach = Invoke-RestMethod -Uri $URLPost -Headers $headers -Method Post -ContentType "multipart/form-data; boundary=$boundary" -TimeoutSec 20 -Body $bodyLines
Write-host "Uploaded extension $ext to port $portNum"
# Need to logoff to clear connections
$URL = "${pabxBase}/Home.htm?sessionId=$sessionID&LOGOUT%28%29"
Invoke-WebRequest -Uri $URL -WebSession $necLogin |Out-Null
Posted in General, PABX | Leave a comment

Sports Administrator

Welcome Screen

Welcome Screen

Sports Administrator: Open-sourced Sports Carnival management software.

I have just released a free open-source Sports Carnival management database that may be useful to schools for running their Athletics and Swimming carnivals.

It has many features with some of the main ones listed below:

  • Easy entry of event results
  • Tracking of event records
  • Cumulative house points
  • Age Champions
  • Multiple Reports to print results
  • HTML export of results

Full feature list here:

This was previously a commercial package, but with the kind permission of Andrew Rogers, the original developer, it has been made open source and I have updated it to work with new versions of Access 2010+

If anyone is interested in a great (free) Sport Carnival software please try this out. I always found it much easier to use than MeetManager which is the main commercial package several schools use.

Latest Release

Source Code

Posted in General, Utilities, Windows | Leave a comment

Avahi Reflector filtering patch

This is about transmitting Bonjour traffic across VLANs using Avahi Daemon as a reflector with a service filter patch applied.

We have multiple VLANs configured for Wi-Fi, and wired LANs and have AirServer terminals connected to classrooms needing to be discoverable from iPads and Macbooks on the Wi-Fi network.

Originally we were just using the standard Avahi-Daemon package provided by Ubuntu, but found as more BYOD laptops and iPads connected the reflected packets became greater and some services we did not want advertised (e.g. home shared printer, music libraries, etc). For this standard config there are multiple guides already available.

To filter out services we do not want I have written a patch to the Avahi code that allows you to enter a list of services you want to be reflected and only those will appear in other VLANs.

I based my implementation on the suggestion in Extending multicast DNS across local links in Campus and Enterprise Networks and it works by filtering out received packets before they are added to the daemon’s cache. All locally published services are allowed through.

Avahi-Reflector-Filter patches. This includes the main Reflector Filter patch and a patch to add _airplay._tcp to the listed services avahi-browse shows.


For those looking to compile in Ubuntu make sure you also apply all the Ubuntu specific patches

I also needed to modify makefile input for make install to work correctly

--- a/avahi-python/avahi/Makefile.in
+++ b/avahi-python/avahi/Makefile.in
@@ -456,7 +456,7 @@
 $(INSTALL_DATA) $$files "$(DESTDIR)$(avahidir)" || exit $$?; \
 done || exit $$?; \
 if test -n "$$dlist"; then \
- $(am__py_compile) --destdir "$(DESTDIR)" \
+ $(am__py_compile) --destdir $(DESTDIR) \
 --basedir "$(avahidir)" $$dlist; \
 else :; fi

--- a/avahi-python/avahi-discover/Makefile.in
+++ b/avahi-python/avahi-discover/Makefile.in
@@ -477,7 +477,7 @@
 $(INSTALL_DATA) $$files "$(DESTDIR)$(avahi_discoverdir)" || exit $$?; \
 done || exit $$?; \
 if test -n "$$dlist"; then \
- $(am__py_compile) --destdir "$(DESTDIR)" \
+ $(am__py_compile) --destdir $(DESTDIR) \
 --basedir "$(avahi_discoverdir)" $$dlist; \
 else :; fi

To compile Avahi on Ubuntu 14.04 I used the following

./configure \
--disable-autoipd \
--enable-compat-libdns_sd \
--disable-mono \
--disable-monodoc \
--disable-qt3 \
--enable-gtk3 \
--with-systemdsystemunitdir=/lib/systemd/system \
--sysconfdir=/etc \
Posted in General | Leave a comment