Introduction
I've owned a Humax FVP-4000T Freeview unit for a
while and overall it performs well and has plenty of useful features (https://uk.humaxdigital.com/product/fvp-4000t/).
It is connected to my home network and therefore the Internet. One small disappointment is the
quality of the Infra-Red remote control handset, with some of the buttons
occasionally being unresponsive or inconsistent in operation. I also own an old
HUDL tablet and I installed the Humax remote control application for Android.
This app has most of the controls necessary to operate the FVP-4000T, but the
screen display isn't optimised for the HUDL, so this can be slightly
frustrating to use. As the control from the HUDL to the FVP-4000T is via my
wireless and wired network, the control is easier and more reliable than the
infra red unit, so it's frequently in use.
I began to wonder whether an app might be
available for Microsoft Windows so I could control the FVP-4000T from my
laptop (running Win 8.1), but my initial searches proved fruitless.
One resource I found proved useful in that some
control codes had been identified that the Humax Android app uses to control
Humax boxes. This can be found at:
This got me thinking about whether I could produce
my own app using the Python programming language. I've only been using Python
for a couple of years on and off, so do not consider myself at all proficient.
There may be some poor aspects of the programming in my script that I developed,
or there may be a better way of doing some of the elements, but I got an
application working how I expected it to work. I've learned a lot on the way,
and I extend my thanks to anyone who has posted Python code snippets or asked
questions or given answers on-line.
There were a number of things I needed to
investigate as part of this project.
1. Find out which network address, protocols and
port the Humax FVP-4000T 'listens' for control codes.
2. What the Humax app that runs on the HUDL sends
and the format of the information used.
3. Create a 'window' based Python application - to
run as a standalone executable on Windows.
For this write up I have assumed the reader
understands many basic concepts relating to network protocols and the Python
programming language. There are many good resources on the Internet which
should help with understanding these topics.
Network
address, protocols and ports
Initially I set a fixed IP address on the Humax
FVP-4000T box so that any network investigations would be easier; the last
thing I wanted was to 'chase' around finding the FVP-4000T network address
every time I worked on this project.
I used Wireshark (https://www.wireshark.org/) installed on my laptop to capture the network traffic sent from my Humax app on
my Hudl tablet to determine the protocols, ports and control codes. I used a
network splitter cable so I could monitor the traffic from my tablet to the
FVP-4000T via my home router.
When the Android Humax app starts it sends out an
SSDP (Simple Service Discover Protocol) message.[https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol]
discovery
The Wireshark capture:
In response, the Humax sends:
Here the Humax IP address is identified and also
the MAC address. This was to prove useful during the development of my app as I
wanted to be able to discover the Humax IP address automatically and confirm
that the IP address is assigned to a Humax unit.
Humax
Control Codes
Running the Android app I experimented using the
'Mute' control - mainly because when using the app control I could hear when
the Humax audio was present or not and didn't need to monitor the TV screen.
Also, just using one function should help to identify the code(s) in the
network data.
The information I confirmed was that the Android
app uses UDP (User Datagram Protocol) [https://en.wikipedia.org/wiki/User_Datagram_Protocol]
to send the control message and the Humax destination port is 22558.
The Wireshark capture:
The next information comprises:
In remote control mode, this data comprises the
control data which is always:
686d78726100000072637500000000003030
Note that the byte codes 72, 63, 75 equate to
'rcu' - a 'pointer' to Remote Control Unit
The code for 'Mute' in this example above is ascii
72, byte codes 37, 32.
The complete control message becomes:
686d787261000000726375000000000030303732
I checked some more of the codes from (https://hummy.tv/forum/threads/ip-remote-commands.7783/)
to confirm that the control and data format was consistent.
Further investigation revealed a different control
data format when performing searches on the Humax.
When using the search function, the control data
becomes:
686d7872610000006b65790000000000332c302c
Note that the control data used in 'remote control
mode' to select the search mode on the Humax replaces the 3030 with 332c and adds 302c.
Also, the byte codes 6b, 65, 79 equate to 'key' - a pointer to 'Key text search'.
Actual search text appears after this control
data. For example, the text letter 'a' appears as:
686d7872610000006b65790000000000332c302c61
(61 byte code = 'a')
All the information to control the Humax FVP-4000T
was now known. I had to investigate how I could use Python3 to develop a set of
control functions within a windows interface.
Python script
The key elements needed:
1. Send an SSDP message to find the Humax box
details (IP & MAC addresses)
2. Confirm a valid Humax MAC address
3. Set up the host PC to send UDP data
4. Construct remote control and search control
messages
5. Create a Graphical User Interface (windows)
with selectable functions
6. Define functions - when user selects, the
script sends the command
7. Enable user to enter search text and send to
the Humax
SSDP Message
I found a Python script which I modified that enabled
me to send SSDP messages. When experimenting with my Humax unit, the message
returned included :
uuid:1E96FF1A-6771-2F44-A0C5-2c088c9931f9
After some investigation I determined that the
elements '2c088c' is an identifier for the Humax MAC vendor ID. On line I found a range of vendor ID Humax
MAC addresses that I included in my application code as part of confirming the IP address associated
with its MAC address. It is intended this app 'should' work with different
Humax boxes if they have one of the vendor ID MAC addresses I've included in
the Python script. See 'Using the application' at the end of this blog if this application doesn't work with your Humax box.
The script extract below creates the SSDP message, sets the SSDP IP, configures the IP socket, sends the message and captures the response(s). It checks for a valid MAC address, extracts the IP address and sets the variable UDP_IP as the 'target' IP address and the port number to 22558 for future messages to be sent to the Humax box.
import socket
msg = \
'M-SEARCH * HTTP/1.1\r\n' \
'HOST:239.255.255.250:1900\r\n' \
'ST:upnp:rootdevice\r\n' \
'MX:2\r\n' \
'MAN:"ssdp:discover"\r\n' \
'\r\n'
msg1 = msg.encode()
ssdp_udp = '239.255.255.250'
# Set up UDP socket
s = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.settimeout(2)
s.sendto(msg1,(ssdp_udp, 1900))
#humax mac ids (valid mac addresses as of May
2018)
macs = (
'e820e2', 'dcd321', 'cc4eec', 'a0722c', '942cb3', '940937', '90f305',
'6cb56b', '4cd08a', '403dec', '3438b7', '2c088c', '2832c5', '08eb74',
'044f17', '000378'
)
# receive ssdp responses
try:
while
True:
data, addr = s.recvfrom(65507) #addr format ('xxx.xxx.xxx.xxx', 1900)
inputtext = str(data)
for
var in range(len(macs)): # check for valid humax mac address
query = (inputtext.find(macs[var]))
if (query != -1):
ipads = str(addr)
comma = (ipads.find(","))
extract = (ipads[2:comma-1]) # get the IP address
except socket.timeout:
pass
#IP is 'extracted' from the ssdp search response
UDP_IP = extract
UDP_PORT = 22558
Remote
Control & Search Control Messages
Previously I stated that the Remote Control
Message format had a static element. This is used here below, one called 'Control' for the Remote Control, the
other 'Controlkey' for accessing the Search function on the Humax box.
#Static HUMAX message element
Control =
bytes.fromhex('686d78726100000072637500000000003030')
Controlkey =
bytes.fromhex('686d7872610000006b65790000000000332c302c')
To construct a complete Remote Control Message,
the function code is added to the Control Message element. For example the Mute
function code is '3732'. The defined Python function looks like:
def Mute():
SendCmd(bytes.fromhex('3732'))
When the Mute function is selected, it then calls
the SendCmd function and passes the hex code to be added to the Control message
element. The complete message is then sent to the Humax box.
def SendCmd(code): # Construct full HUMAX control
message & send
Message
= Control + code
sock =
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(Message, (UDP_IP, UDP_PORT))
I created a series of functions to cover the range
of remote control functions available on the Humax. These functions, together
with the remote control codes are viewable in the Python script.
Programme
Search
To construct a Remote Control Message to do a
programme search on the Humax unit, first a Control Message is constructed and
sent to the Humax box. The function to
do this is below which opens up the search box on the Humax. (I include the Windows GUI code, which
creates a search text entry box for the user to enter text, a graphical button
to submit the entered search text to a Python function to extract the entered
text, construct the text string and send to the Humax unit).
# search function creates text entry box &
submit button
def Search():
SendCmd(bytes.fromhex('3437'))
global
textstr #global to be available to Dialog function
textstr=StringVar()
textentry=Entry(myGUI, width=12, textvariable=textstr).grid(row=1,column=6)
button1=Button(myGUI, text='Submit', width=6,
command=Dialog).grid(row=2,column=6)
# get entered text & format to send to Humax
box
def Dialog(): # send ascii letters to Humax search
box
text =
str(textstr.get()) # get text search entry box
c =
len(text) # variable for string length
pointer
= 0 # starting point in string
cs =
"" # hex string construction variable
while pointer < c:
#
construct hex characters string without leading 0x, from decimal values of text
cs=cs+(hex(ord(text[pointer])).replace("0x",""))
pointer = pointer+1 # move along the string
SendKey(bytes.fromhex(cs)) # use constructed string as the 'code' to add
to the 'control' msg
def SendKey(code): # Construct full HUMAX search
control + text & send
Message
= Controlkey + code
sock =
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(Message, (UDP_IP, UDP_PORT))
Windows
Graphical User Interface (GUI)
The interface I created is fairly simple and
clean, and laid out to suit how I use the controls. The Python code can be
modified to create a different layout.
When the Search 'button' is selected, the text entry
box and submit button appears.
To create the windows interface:
from tkinter import *
# create Window GUI
myGUI = Tk()
myGUI.geometry('550x215')
myGUI.title('HUMAX FVP-4000T Remote Control IP: '
+ extract) # include IP address in header
myGUI.resizable(False, False)
All the main Python code follows this code including
all the functions and GUI graphical elements.
The final line should be:
myGUI.mainloop()
Within the Python script I include all the codes
and functions that worked and those which didn't.
Using the
application
If you have Python3 with the Integrated Development Environment (IDE) installed on your machine you
can download my script, open it in the IDE and run from there (use F5).
Alternatively download the GUI.exe application below with all the associated files in the 'GUI' folder. When run, a command window
will open as well as the GUI window. Research indicates it's possible to run
the GUI while suppressing the command window, but I haven't got this to
function correctly.
This executable and associated files was made
using the application called PyInstaller. PyInstaller converts a .py script to a Windows
executable, standalone .exe file.
This can be found at: https://www.pyinstaller.org/
If my PyInstaller files do not work on your
machine I suggest downloading PyInstaller on your own machine, and use it to
convert my GUI.py script on your own machine.
When running the application (ensure your Humax box is powered up) there will be a short
delay of about 3 to 5 seconds as your Humax unit is identified, then the GUI
appears. The GUI will not appear if your Humax unit is not identified if your Humax
unit MAC address is not listed in my Python script.
One work around to overcome this:
Modify my Python script to remove all the code
associated with the SSDP function. In the code element (UDP_IP=extract),
replace 'extract' with the IP address of your Humax unit. It's recommended that
the Humax unit is assigned a fixed IP address.
Alternatively, modify the Python script to include
the MAC address element of your Humax unit. This address should be on the unit.
The GUI controls should be straightforward to use.
When using the Search function, the Humax search
screen appears. After entering the text in the text entry box on the
application and selecting Submit, the text should appear in the Humax search
box. Selecting Search again on the application then 'applies' this as the
search text on the Humax box and the search results should appear.
The Power button only works to turn the Humax unit
power off.
Navigation around the Humax functions is by the
Left, Right, Up and Down buttons. CH- and CH+ work to change programmes, but
also scrolls screens when viewing the TV
guide or Recordings listings.
I hope you find this application helpful. If you don't like any elements of the
programme you are free to modify it to your own taste.
If you find it useful please leave some comments
on my blog.
Downloads:
Downloads: