Monday 8 January 2024

What to do with an old iPad?

How old?

I have an iPad and an iPad mini which were in occasional use recently by aging relatives:

  • third generation iPad model MD328/A1416 (16GB, 9.7" screen)
  • first generation iPad mini MD528/A1432 (16GB, 7.9" screen)

Both models were first marketed by Apple in 2012, so they are really old in computer terms.
Both appear to be working well with acceptable battery life, however their software cannot be updated from iOS 9.3.5, iOS 9 was released in September 2015.  It was superceded by iOS 10 a year later and the current version is iOS 17.

Owing to their old software they have no real value, even on eBay, and worthy causes are not interested in them as a donation.   I am reluctant to dispose of them and I have been thinking how they could be useful.

Which apps can be installed?

The short answer is that existing apps should continue to work but practically nothing on the Apps Store can be installed on iOS9.
  
The apps store normally only allows recent versions of apps to be installed  and they are all post 2015.  The one exception is that, for apps which have been previously purchased / installed, an old version can be downloaded.  This provides a loophole which can be exploited: purchase or install a new app on your newer iPad then go back to the old one and install an older version, if it exists.

A third option is to jailbreak the iPad so that you can install apps from (possibly dodgy) non-Apple sources but I that is a slightly different subject so I wont investigate at this point.

It seems unnecessarily restrictive for Apple to prevent downloading old versions, but to be fair, this is ten year old software and hardware so it is unlikely that you need apps that you havent needed in the last ten years.

Which apps work?

Even if an app is installed on the iPad, it may not work properly.

Apps which are included with iOS9 appear to work well (Safari, camera, iBooks, photos for example).  If an app is purely local and doesn't require internet functionality it should work.  I had a few old games (The Room, Sudoku, Labyrinth and Candy Crush) which are ok.  

Apps using local networking (Heos, webSsh, Unix-Reflection, Pure Connect) may work and some  which use the internet are available.


To get an idea of which apps I can potentially use I went through the list of purchased apps on the App Store and tried to install them all on the old iPad.  I started them up and ran some quick tests.  A number of apps, including Chrome, failed at this stage, as they carried out internal checks and decided they needed (unavailable) upgrades to use.


At the end of this exercise my ipad has extra apps shown to the left.  The main apps which use media are likely to be the most useful: vlc (video), photos and LiveFrame (slideshows), safari (for browsing and my local web pages), Heos, iPad Music and Pure (for music control).

There is no file manager function available on iOS9 but it does include iCloud so you should be able to play music, videos, photos etc. and store them locally within applications.

In summary, although the choice of software available is very limited, we do have most of the functionality we may need.  If an extra function is required, I can search the web for suitable iOS9 compatible apps.

Potential Users

I already have a decent, newer iPad 5th generation MPGC2B which is a great tablet, although it has just stopped receiving upgrades (it is still on iOS16), so an older heavier iPad isn't going to be an acceptable substitute for me.  If I do use an old iPad it is likely to be dedicated to a single function, watching movies, controlling the music player or home automation.

Alternatively, the iPad could be useful to someone who currently doesn't have their own.  My elderly father-in-law Roy may like to use it as a family photo album, to help with his memory.  Grandson, Harry Junior might like his iPad to watch youtube videos or play childrens games.  Again they are likely to use for a single app rather than a general purpose device.

The iPad and iPad mini have great quality screens and good sound so they are very suitable for any audiovisual media: music, videos, photos, books.

By switching the iPad to never auto-lock, it retains the last application screen when you start to use it.  This minor configuation change makes quite a large difference; say when resuming a video, you can just turn it on and continue to watch, without all the faffing around of starting the app and finding the right place.
Similarly, for an audio controller it is almost as easy as a remote control, just turn on and your control screen is in front of you.

Potential Uses

1    Photograph slide show

Either the Apple photo app or LiveFrame can provide an ongoing slide show.
The show can be made continuous and the photo collection can be as large as you like.
Suitable for an elderly relative.

2    YouTube web

As a dedicated YouTube player, the iPad is very easy to use without adult assistance.
Suitable for a young child

3    ebook reader

I typically read a single book at any given time and there is a plentiful supply of ebooks and pdf documents to read.  The app provided by my local library for online reading also works.

4    Video player

The excellent quality screen is perfect for playing videos with VLC.
As usual, dedicating the device to this function makes it much easier to pick up and put down current viewing.

5    Music Controller

The iPad mini makes a very good smart remote control.  I have web pages to control my different sorts of music and if I leave the iPad mini lying around it is actually more convenient than a remote.  The only downside is to periodically plug it in when not in use.  My web pages also work on peoples phones but it is easier to pick up a small device than starting your browser and choosing an appropriate link.

6    Game Player

I dont often play games, but if an old iPad feels like an entertainment centre I may reach for it to play a game (or watch a film, read a book etc).

7    Home Automation Controller

This is a very good new / developing area for me.  Although the iOS9 doesn't support Home Assistant it may be possible to set up as a central control device.

8     Linux terminal

When working on a variety of linux tasks, it would be useful to have a dedicated virtual keyboard/screen for "headless" systems.

9    6502 Console

This is likely to be a rare but sexy usage to provide a screen and keyboard for my 6502 system.

10   Jailbreak

An old redundant iPad would be ideal to see how easy / useful it is to jailbreak a device and whether it makes a big difference to what you are able to do with it.


The trouble with bright ideas is that they may never be used.  Rather than choose one of the above functions I may play with a variety and see if any of them are properly useful.

Conclusion

I started off thinking it is a shame that good working technology hardware needs to be thrown away to be recycled.  After this exercise I am hopeful to extend the life of the iPad-3 and iPad mini-1 for a couple more years.

I will be very happy if I do find a proper use for these little gems.  There may be a future blog post describing any successes.

Saturday 6 January 2024

Sonos : Web Page Control

Rationale

We have made excellent progress controlling Denon and Heos functions through python and web pages.  Playing albums using my web page only requires 3 clicks and is much easier than navigating through the NAS folder structure using Heos.  

I previously thought that Sonos was mainly limited to a closed eco-system of Amazon Music, Spotify etc but as I begin to look in detail I see that it can do a lot more, allowing you to play music from your own devices or a DLNA server.  The Sonos app, although it is slightly better than Heos, requires you to navigate through a menu structure to play albums.  Sonos actually provide an excellent in depth developers guide, if I want to develop from scratch.

My first objective was to see whether I could play RPI/mpd output on Sonos and I found that I could do so using TuneIn (Classic).  This was my fall back plan when my Sony amplifier was misbehaving, before I bought the Denon receiver.  It turned out I didn’t need to use Sonos for that purpose but the Sonos Beam is still available to me.  My temporary solution was somewhat limited as Sonos treats RPI/mpd input as a stream, whereas I really want to play an album using Sonos controls for viewing playlists, skipping tracks etc.

Investigation

Previously the usage of python for back-end programming and javascript / web sockets in the web page worked really well for the Denon  / Heos control page so I will use this approach again.
Sonos is more widely used than Heos and, searching on github,  SoCo is amongst the most popular Sonos projects.  It is a general purpose client written in python, currently maintained and includes excellent documentation so it seems perfectly suited for my needs.

 

Installation is trivial ("pip install soco").  Browsing the documentation I could see that the music_library submodule will be most useful to me. I was quickly able to check using just three statements in interactive python that I can use SoCo in conjunction with my Sonos Beam:

Music Storage

I have recently installed a rather excellent TP-link ax5400 router which has a USB port.  It has the capability to share music / video / data held on a USB flash drive.  I actually attach an unpowered 4-port USB hub which allows me to utilise multiple drives.  This gives me a free NAS capability and easily allows me to keep all music in one place.

Sonos likes good quality mp3 tags to scan so that it can scan music in an orderly manner.  In particular, my charts downloads often have the artists name both in the "album" tag and the "album artist" tag, which causes Sonos to think I have hundreds of albums each with one track.  In addition Sonos plays tracks in filename order if there are no track numbers.  I fixed these problems using the wonderful windows mp3tag utility by setting "album artists" to "various artists" for chart downloads and by adding a track number.  Sonos is now pretty good if I navigate through the music library by artist or album and play all tracks in an album.

Unfortunately Heos displays the "album artist" field to display tracks so lots of my tracks now appear as "Various Artists".  The only solution I can think of is to have separate charts folders for Heos and Sonos.  Ah well, it is not a perfect world.

 Soco python programs

SoCo is an excellent python module, which is enhanced by the comprehensive Sonos API and quality documentation.  It proved to be straightforward to set up my own functions to choose and play albums.

Although it is easy to choose an album by artist I prefer to use the folder structure to define my album playlists.  This allows me to navigate to an album and play it even if the mp3 tags for album, artist are imperfect.  

The following interactive python example shows how to navigate the router music flash drive folders to choose an album to play.  The  "root" level is a shared folder "//RouterShare/H" which is obtained using a command "get_music_library_information('share'). The music at each successive level in the folder structure is achieved by selecting an array element from root, level1, level2, level3 by browsing the folder object retrieved at the next level up.

Having chosen a level3 item, which is an album, it can be added to the Sonos music queue and played as shown below.


These commands can be integrated into our command line python program which can return a list of groups, artists or albums.  The list is typically passed to the web page which returns with a choice indicating the next level. Once the group/artist/album selection has been made their values are passed to the play option and python makes a Denon request to add the tracks to the queue and play the album.

Web page


Our web page simply has to display available groups, artists and albums and allow the user to choose what they want.
Initially the "Show Groups" button is used to make a python request to Sonos which returns the list of groups that are inserted into the page.  In due course this will be done automatically on loading the page.  In the HTML you see that the web socket command is still denon, as we are using the framework setup for Denon and Heos.  The "-l groups" parameter causes python to return a list of the groups.  For convenience all the commands include "-g" (group chosen) and "-a" (artist chosen) parameters as well.  

This web socket command  causes the python command "sonos.py -l groups -g 0 -a 0" to be executed and the list of groups "A-B", "C-F", ... is displayed.








Clicking on a group causes a Sonos request which fetches and displays an artist list.  Click again and a list of albums from that artist is displayed.  A third click puts the album on the Sonos play queue and starts the music.





The Chrome console log is very helpful when testing the web page.  In the example to the right you can see information returned to the web page after clicking the "show groups" button, then clicking "C-F" then "Coldplay".  At each stage the next level of information is returned and displayed on the web page.  The information from Sonos is returned in JSON format so that it can be easily interpreted in javascript.

My python script adds some extra fields at the end of JSON returned by Sonos.  In the example on the right the second JSON response has
 "Sonos" : "albums", "group":"1", "artist":"4"
added to show that the information is a list of albums relating to group1 (C-F) and artist4 (coldplay).

When a Sonos response is received by the web page web socket ws.onmessage function the javascript code looks at the "Sonos":"albums" field and can see that the list of albums should be placed in field #sonos7 which is below the group / artist fields.
makeSonosList formats the json as html fields and buttons so that it looks correct and reacts correctly.


If the level is groups or artists the html button causes a request for the next level of information.
If the level is album, the button submits a request to play the appropriate album.



Monday 18 December 2023

Denon : Using Heos

Intro

In the previous two Denon posts I have described setting up a web page which duplicates most of the functions available in the Denon AVR App, tailored to my own needs.  I now want to see what the Heos app can do for me and which functions I can access through my own web page.

Heos is a brand associated with smart speakers, I believe it is owned by Denon/Marantz.  When you switch the amplifier to Heos input it functions like a Smart Speaker.  Heos, like Sonos, needs you to choose what to play and to control the speakers.   Heos has Android and Apple apps which allow you to choose music and change volume, pause, skip tracks etc.  Heos and Sonos focus on Streaming Services such as Spotify and Amazon for their music input but they additionally allow you to add music from local files or shared folders.  I am most interested in playing my own music, more specifically I want to easily select and play albums.

What can Heos do?

Heos can select the standard Denon inputs for you:

  •  AMUSE
  • CD
  • JONGO
  • Media Player (USB)
  • Tuner.

In addition it can play:

  • Local music from the iPad / phone running the Heos App
  • Music from a network DLNA shared folder.  For me this is principally my Router USB Media Server (which is called RouterShare).
  • USB (which is a stick plugged into Denon)
  • Heos playlists which you can define (by saving a list of items off the playing queue).  These playlists are stored on your cloud HEOS account, so they available on all apps.
  • TuneIn radio stations, including a list of favourites
  • Music Services such as Spotify or Amazon Music
  • Recently played tracks can be selected.
When music is playing you have standard options to control:
  • volume/mute
  • pause/resume
  • next/previous
  • shuffle/repeat
  • save current music queue as a playlist
When a track is playing you can see time elapsed, time remaining, a list of items on the queue

My particular objective is to be able to select and play albums from the RouterShare DLNA server.

Development Approach

Of course I would prefer to build on someone elses solution for my Heos web page.  The approach used to develop Denon functions entailed providing a python client as a back-end and invoking those functions from a web page using javascript and web sockets.  Separation of front-end and back-end programming was very successful, making testing and fault-finding comparatively straigh-forward, so I followed the same approach for Heos.

Github is now the principal repository for open-source projects.  Searching for "Heos" on github yielded heospy as the most appropriate python project.  However I found that the Heos CLI protocol specification gives me the information I need.  It explains that you telnet into Denon port 1255 and use commands in the format heos://player/get_players to communicate with Heos as shown below

As an aside, my development environment is Windows and there is no official Heos "app" for Windows.  Some forum posts recommended "Heos Remote" which is a very simple client available from the Windows Store for £5.  I bought a copy which allowed me to quickly see what was happening inside the Heos environment.


Heos Web Page PoC

When programming Denon XML back-end I tried out the python Telnet package interactively.  It is quite simple to adapt this to my needs so that the function telnet_client writes a request to the Denon Heos port and reads the response.



There were a few formatting wrinkles to resolve to make the data format acceptable for input to javascript on the web page.  Firstly the response above includes plenty of newline characters so it is readable.  We use the command heos://system/prettify_json_response?enable=off
to remove them.  Secondly our JSON conversion alters double quotes to single and we have to put them back to doubles.  Once this has been done we have our JSON ready to pass to the web page.

Our mechanism for invoking python scripts from a web page using web sockets is now well established.

Step 1: On the web page make a web socket request using the javascript we.send command.

Step 2: Web socket processing sees the denon command and calls the python denon script.  Within this script the "H" parameter causes our heos.py script to be executed.

Step 3: Output from heos.py is returned to the web socket and is captured into the web page within the ws.onmessage function as event.data.  ws.onmessage parses event.data input into a JSON structure myobj

Step 4: javascript jquery function inserts appropriate output from myobj into the web page.
In this PoC example there is a top level json field payload which is an array.  We take the first element of the array and insert the name field into html field "heos1".  In fact this displays the name of the Denon receiver "Bruce" in the web page.  I called the receiver Bruce as Google home had a problem understanding the (rather dull) name Denon.

We have now successfully demonstrated that we can execute Heos commands and utilise output in our web page.


I found a useful summary of Heos commands we can use in a github project heospy


Heos Control commands

Now we have a working mechanism we can start adding useful stuff.
We add buttons for play, pause, stop, next, previous and status.  The status button will update information on the current song, artist and track.

In python we send appropriate commands to Denon for the corresponding functions:
The "status" parameter causes Denon to provide song, artist and track info which is received by ws.onmessage and added to the web page.
The console shows information received and the web page is updated:

Playlists

Heos allows you to setup your own playlists.  These are stored in the cloud under you Heos user name so that they can be selected from any device.  To put an album or a selection of tracks on a playlist, first place the tracks on the queue and then save the queue to a named playlist.  I dont expect to use them much but it will be helpful to be able play predefined playlists.

As usual we ask Denon to provide the information to us. The system identifier for playlists is sid=1025.
We issue a browse command for sid=1025 and a list of playlists is returned.  We can then play that list by quoting the container id e.g. cid=555631
I set up two sample playlists and hard-coded their cids so that I could use a python command to start playing them:

Heos Commands to play an album

The main use of Heos is to play albums.  I have migrated my commonly used albums to a DLNA shared folder on the router called RouterShare.  I have defined this folder to Denon so that we can access an play those albums.  My albums on RouterShare are ordered on three levels of folders, at the top level are alphabetical groups of artists, the middle level has a list of the artists in that group  and the bottom level has albums by the artist.  The files in bottom level folders are the album tracks.
For example "Taylor Swift" is a folder in the T-Z group and includes the album "evermore (2020)".

The Heos Browse command is used to determine the content of DLNA shares, which are defined as system identifier sid=1024.  The telnet output below shows that there are two DLNA shares and RouterShare has sid= -1779289667.  Browsing sid=-177928667 shows that "Browse Folders" has a container id cid=64.  
If we look at container cid=64 we see that cid=64$2 contains our music and cid=64$2$5 is the folder
T-Z

Going down a couple more levels shows us that "evermore (2020)" has cid=64$2$5$0$0 and browsing this cid shows us the identifiers of individual tracks, for example the first track "willow" has mid=64$2$5$0$0$0.

We can play the album by adding cid=64$2$5$0$0 to the Heos with the Heos Command
where pid is the physical id of the smart speaker, sid is the device containing the music and cid is the container whose contents we want to add.  The last parameter aid=4 says that we want to replace whatever is on the queue with our new content.  Optionally this track list could be saved as a playlist.

A summary of these commands is shown below.

Album Play Code

Python code is quite straightforward, we add two new functions album_list (show contents of a container) and album_list (add a container contents to a queue).

The "-g" command displays the contents of a container and "-a" adds the contents of a container to the play queue. "-g root" displays contents of the top level container "64$2".

We add some extra fields to the web page "heos5" through "heos9" so we can display album and track playing information.
When the page is loaded the top level folder structure is shown as buttons in cell in heos5.  Clicking on one of these buttons causes a list of artists to be displayed in cell heos6 and clicking on an artist displays their albums in cell 7.  Clicking on the album causes it to be placed on to the Heos queue and start playing.
The web page below shows part of the html when the "T-Z" button has been pressed.  It shows that if the "Taylor Swift" button is pressed a web socket command is made to obtain the contents of container 64$2$5$0, which comprises a list of Taylor Swift albums.
A javascript loop is used to format each array element to a button for the next level.  At this stage the javascript becomes somewhat incomprehensible so I wont include the details.

Current track and Queue


The final piece of the picture for a Heos Music Player is to display the track which is currently playing and a list of tracks in the queue.
The appropriate Heos commands to display the current queue and display the current track are shown below.
In fact, at the time of writing, the queue command only passes the first six tracks to display on the web page.  I will fix the problem in due course.    Current track is stored in heos9 and the queue is inserted as field heos8.

Endnote

We now have a fully functional Heos music player which is tailored to my requirements.  You are quickly able to select and play any album from the shared folder on the router.  This has greatly exceeded my expectations.  It provides the functionality of my existing AMUSE music player, it uses the Router "NAS" folder and can be integrated with Denon controls on a single page.  In fact, once I have tidied it up it will probably replace the RPI mpd solution completely.





Monday 20 November 2023

Denon webpage - xml

Intro

Using a github python package from ol-iver I have been able to implement the main amplifier-related Denon functions I want on a web page.


Unfortunately I want more!

It must be possible to obtain from Denon, all the information which is available in the Denon iPad/Android app.   In particular, the app shows a list of all radio station presets from which you can select.  In the protocol specification I did find a telnet command to change preset but there isn't a command to list all presets.

There is a mention in ol-ivers github page of how he used  wireshark to identify XML commands which retrieve information from his Denon AVR.  I took this as a hint that I must use Network packet capture to discover further commands which we can use.  There was also a reference in a Home Assistant Denon forum to the "unofficial port 80 HTTP protocol" which may provide XML using an http request. 



Packet Capture

If I start up wireshark and monitor all packets coming to / from Denon I see nothing useful.  Both the iPad and Denon are on the wifi network and wireshark is running on the PC with an ethernet card or wifi.  Windows isn't very good at putting network cards into monitor / promiscuous mode so I wasn't sure it was capable of doing the capture I want.

Previously I have had better success with a packet analyser called Fiddler.  In particular, it is possible to set up Fiddler on the PC as a proxy server and then configure the iPad to send all its packets via Fiddler.
My copy of Fiddler hasn't been used for a few years and doesn't seem to work.  "Fiddler Everywhere" is a new version of their software which is available for a 10-day free trial, it can also capture https traffic.
I installed Fiddler Everywhere and found it is quite easy to use and works very well.  In fact I will subscribe for a couple of months ($12pcm) as I think it is worthwhile.

Fiddler shows exactly what I want to see, a lot of http requests/responses to/from Denon together with the appropriate message headers and bodies.  The message body is in XML format.  

Packet Analysis

Most of the packets are in POST format but a few are GET requests.  The good thing about GET requests is that you can enter them directly into the browser.  I tried submitting a sample request and Denon responded with an answer written in XML 😀😀

POST requests typically need to be sent using a browser HTML form, but I use an excellent little utility, postman which submits requests and receives responses.  Now I can submit a POST request and receive a response.

Looking through the (hundreds of) requests I saw that most of them are covered by ol-iver's functions.  The POST request which I really want is "GetTunerStatus" which I will describe further below.

Some of the GET requests are interesting.  As shown on the screen shot below, commands within the Denon Protocol specification can be submitted as  a GET request, which is potentially much simpler than ol-ivers telnet interface.  Packet 1449 shows a HTTP GET request to change the radio station preset to number 6.

I can easily add some buttons on the webpage to change the preset value:


Packet 1238 shows a POST request to list all presets and the xml response which is provided

Web page

When I tried any POST requests in my web page I received messages to say that they were blocked owing to CORS (Cross Origin Resource Sharing) security weakness.
If I wanted to I could circumvent this issue by starting chrome on windows with a switch to allow CORS, but I felt this wasnt the way to go and I abandoned the idea.

Instead I returned to python, found a geeksforgeeks explanation for submitting HTTP POST requests and tested whether this would resolve the problem.  Success 😀😀, Python responds with a chunk of XML containing the information I need.
Initially I imported XML to JSON (xmltodict) to make the data accessible but I ended up just searching on the <name> tag to extract the current radio station.

CGI

Rather than submitting web requests using the websocket interface I felt it would be more sensible to use the lighttpd web server python CGI interface.  My python script needed some tweaking to work in CGI (xmltodict didn't work) but I was soon able to report the tuner preset back to the web page.  This was inserted in my draft web page into an iframe.  An early draft is shown below (before formatting).

Result

We now have a properly functional single, simple web page which has all my Denon controls easily accessible and controllable.