Muutimme uusiin toimitiloihin kesäkuun alussa ja muuton yhteydessä löysin vanhan iPod Touchini toimistolta lojumasta. Laite on 64 gigan tallennustilalla varustettu kolmannen sukupolven Touch vuodelta 2009. Ikää kun oli kertynyt jo yli kymmenen vuotta, olin jossain määrin yllättynyt huomatessani, että härveli toimii käytännössä täydellisesti - akku tokikaan ei enää ole ihan täydessä terässä, mutta kyllä sekin vielä jonkin verran varausta pitää. Mobiiliksi viihdelaitteeksi tuosta ei enää oikein ole, mutta jotakin käyttöä sille oli pakko keksiä!

Olen rakentamassa kotiimme e-paperinäyttöistä sääasemaa. Tuon sääaseman käyttöön tarvitsen palvelimen, jolta säätiedot voidaan ladata mahdollisimman yksinkertaisessa muodossa. Tarkoituksenani on käyttää OpenWeatherin One Call -rajapintaa säätiedon lähteenä. Näinpä ajattelinkin yhdistää nämä kaksi projektia yhdeksi: teen iPod Touchista sisäverkossa toimivan säätietopalvelimen.

Tutkin joskus takavuosina iPodin käyttämistä HTTP-palvelimena. Tuolloin laite oli minulla aktiivikäytössä, joten palvelimeksi se ei olisi juuri joutanut. Lisäksi minua hirvitti se, että ilman Applen siunausta olevan koodin ajaminen laitteella olisi vaatinut ns. jailbreakin tekemistä. Tämä oli jo tuolloin varsin idioottivarma prosessi, mutta en halunnut ottaa riskejä. Nyt, kun laite oli poistunut käytöstä ja muuttunut lähinnä jätteeksi, oli tilanne täysin eri. Lisäksi löysin verkosta Krishna Sudhakarin blogikirjoituksen, jossa hän kuvaa erittäin hyvin iPod Touchin valjastamisen web-palvelinkäyttöön. Homma vaikutti tuon blogikirjoituksen perusteella epäilyttävän yksinkertaiselta.

Jailbreak

Aivan aluksi iPodille piti saada tehtyä jailbreak ja asentaa siihen Cydia-"sovelluskauppa". Jailbreak on prosessi, jolla iOS-laitteesta poistetaan Applen luomat rajoitukset asennettaville ohjelmille. Tämä siis tarkoittaa, että jailbreakin jälkeen laitteella voidaan ajaa mitä tahansa koodia, joka on käännetty toimimaan kyseisellä järjestelmäarkkitehtuurilla ja käyttöjärjestelmällä. Cydia puolestaan on sovellus, jolla voidaan asentaa jailbreak-murretulle laitteelle valmiita, yhteisön tuottamia ohjelmistopaketteja. Cydia käyttää taustalla APT-paketinhallintaa ja on siinä mielessä hyvin samanlainen kuin esim. Synaptic. Cydia toimii suoraan iOS-sovelluksena ja sen käyttö on kosketusnäytöllä kohtuullisen helppoa ja miellyttävää.

Suurin ongelma oman iPodini jailbreak-prosessissa oli se, että kyseinen iPod on varsin antiikkinen laite. iPod on tosiaan vuodelta 2009 ja sen iOS-versio on 5.1.1 vuodelta 2015. Koska käytännössä jokaiselle iOS-versiolle on omat versionsa jailbreak-työkaluista, ovat myös nuo yhteensopivat jailbreak-työkalut vuosien takaa. Ja se taas tarkoittaa, että ne vaativat toimiakseen isäntäkoneen käyttöjärjestelmältä varsin hyvää taaksepäinyhteensopivuutta.

Jailbreakin isäntäkoneena minulla toimi uudehko macOS-kone. Olin etukäteen täysin varma, että jailbreak ei tuolla käyttöjärjestelmällä tule onnistumaan, koska Apple on vuosi vuodelta yhä aggressiivisemmin ajanut macOS:ää suljetumpaan suuntaan - paljolti taaksepäinyhteensopivuuden kustannuksella. Olin oikeassa: jailbreak ei onnistunut Absinthe-työkalun macOS-versiolla. Vaan onnistuisiko virtualisoituna jollakin muulla käyttöjärjestelmällä?

Ensin päätin kokeilla virtuaalikoneella, joka ajaisi Ubuntua. Tappelin aikani Ubuntun kanssa, mutta en päässyt oikein edes alkuun homman kanssa. Siirryin seuraavaksi Fedora-jakelun pariin. Fedoran kanssa pääsin kohtuullisella vaivalla vähän pitemmälle, vaan maali jäi häämöttämään kaukaisuuteen vielä senkin kanssa. Tässä vaiheessa myös Linux alkoi tuntua väärältä alustavalinnalta tähän jailbreak-projektiin. Hmm... Microsoftin jääräpäisestä taaksepäinyhteensopivuushalusta voisi oikeasti olla tässä tapauksessa hyötyä. Pitäisikö kokeilla Windowsia?

Windows osoittautui tällä kertaa kaiken avaimeksi! Parallelsilla virtualisoidulla Windows-virtuaalikoneella homma sujui kuin tanssi ja aikaisemmat jailbreak-huoleni olivat osoittautuneet aiheettomiksi. iPod toimi jailbreakin jälkeen täysin entiseen malliin, eikä eroa aikaisempaan huomannut muusta kuin siitä, että nyt laitteella oli asennettuna Cydia-sovellus. Erinomaista!

Pakettien asentaminen Cydialla

Aikaisemmin mainitsemani blogikirjoitus esittelee hyvin kaikki web-palvelimen pystyttämiseen tarvittavat paketit sekä asetukset. Kirjoituksen mainitsemaa KeepAwake-pakettia ei löytynyt enää mistään minulla tiedossani olevasta lähteestä, joten etsin sille korvaavan paketin. Tämä onnistui Cydialla helposti ja sain Insomnia-paketilla toteutettua täysin vastaavan toiminnallisuuden, eli sain iPodini pysymään kiinni WLAN-verkossa myös silloin, kun laitteen näyttö on sammutettuna.

Koko homman tähti on tietysti touch-lighttpd-php-mysql-pakettikokonaisuus, joka asentaa Lighttpd-HTTP-palvelimen, PHP:n sekä MySQL-tietokantapalvelimen. PHP-versio on lähes kivikautinen 5.3.29, mutta mielestäni tämä sopii projektin luonteeseen: ei tässä mikään muukaan kovin tuoretta tai modernia ole.

PHP-versio on lähes kivikautinen 5.3.29, mutta mielestäni tämä sopii projektin luonteeseen: ei tässä mikään muukaan kovin tuoretta tai modernia ole.

Sääsovelluksen rakentaminen

Asetin iPodilla ajettavalle sääsovellukselle kaksi tavoitetta:

  1. Toimia sisäverkossa tietolähteenä Arduino-pohjaiselle sääasemalle. Erityisen tärkeää on etenkin sääaseman kehitysvaiheessa, että säätiedot jäävät välimuistiin iPodille, jotta Open Weatherin rajapintaan ei mene turhia pyyntöjä. Koska sääasemalla ajettavasta koodista ei projektin alkuvaiheessa ollut vielä selkeää käsitystä, tuntui hyvältä ajatukselta myös se, että iPod voi tarvittaessa tuottaa säätiedoista yksinkertaistetun version sääasemaa varten.
  2. Tarjota yksinkertainen ja informatiivinen selainnäkymä asuinpaikkamme säätietoihin. Tämä vaatimus syntyi tarpeesta nähdä helposti asuinpaikkamme säätiedot ilman yhtään ylimääräisiä klikkauksia. Koska talomme pysyy hyvinkin vakaasti paikallaan, on kaikenlainen paikkatiedon käsittely tässä tapauksessa tarpeetonta.

Ensimmäisen tavoitteen saavuttaminen edellyttää, että iPod saa noudettua säätiedot Open Weatherin rajapinnasta ja tallennettua ne välimuistiinsa. Normaalisti käyttäisin HTTP-pyyntöjen tekemiseen PHP:lla cURL-kirjastoa, mutta tälle ei ole touch-lighttpd-php-mysql-paketin PHP:ssa tukea. Onneksi kuitenkin tarpeen oli vain tehdä yksinkertaisia GET-pyyntöjä, joten ongelmaan oli helppo ratkaisu: PHP:n asetuksissa oli oletuksena päällä allow_url_fopen, jolloin HTTP-pyynnöt onnistuvat yksinkertaisesti esim. PHP:n file_get_contents-funktiolla. Vanhan koulukunnan PHP-kehityksen tuoksu on vahva.

Välimuistina päätin käyttää yksinkertaista tiedostojärjestelmään perustuvaa toteutusta. Rajapintaan tehtyjen pyyntöjen vastaukset tallennetaan välimuistihakemistoon niin, että tiedostonimi on rajapintapyynnön osoitteesta luotu MD5-tiiviste. Jos välimuistitiedosto on alle tunnin ikäinen, se toimii tietolähteenä - muissa tapauksissa pyyntö tehdään rajapintaan ja tiedot tallennetaan jälleen välimuistiin. Maailman yksinkertaisin välimuistitoteutus, mutta se toimii tässä tapauksessa aivan riittävän hyvin.

Toisen tavoitteen saavuttaminen olikin sitten erittäin perustason PHP-koodailua. Koska tarpeena oli tuottaa vain yksi HTML-dokumentti, joka näyttäisi aina säätiedot yhdessä ja samassa formaatissa, ei millekään erityisen hienolle template-systeemille ollut tarvetta. Ihan menneiden aikojen PHP-sekasotkuihin en kuitenkaan halunnut vajota, vaan pyrin pitämään koodin edes jokseenkin siistinä. Säätietonäkymät koostuvat erilaisista "komponenteista", jotka tuottavat oman HTML-koodinsa yksinkertaisimmalla mahdollisella template-järjestelmällä: PHP:n sprintf-funktiolla.

Välimuistin päivittäminen tapahtui sovelluksessa alunperin synkronisesti aina sovellukselle tehtyjen pyyntöjen yhteydessä. Jos kävijä teki pyynnön sovellukselle niin, että välimuistissa olevaa tietoa ei voitu käyttää, piti kävijän odottaa, että tiedot ladattiin ensin Open Weatherin rajapinnasta. Koska tietojen lataaminen rajapinnasta kestää useita sekunteja, kesti myös sivun lataaminen iPodilta tarpeettoman pitkään ja käyttökokemus kärsi. Päätin korjata tilanteen mahdollisimman yksinkertaisella tavalla: lähetetään sivu kävijälle osissa niin, että kävijä saa edes jonkinlaisen palautteen järkevässä ajassa.

Vastauksen pilkkominen osiin toimii seuraavasti:

  • Kävijälle lähetetään välittömästi HTML-dokumentin alkuosa, jonka viimeinen elementti on latausilmoitus. Latausilmoituksessa on pieni hyrrä, joka kertoo kävijälle, että jotain tapahtuu.
  • Kun tiedot on ladattu, lähetetään kävijälle loput HTML-dokumentista. Dokumentin lopussa on pätkä CSS:ää, joka piilottaa tuon aluksi näytetyn latausilmoituksen.

Ratkaisu on objektiivisesti katsottuna kamala, eikä noin voisi oikeasti tehdä missään todellisessa sovelluksessa. Homma kuitenkin toimii hienosti ja käyttökokemus on huikeasti parempi kuin aikaisemmin.

PHP-sovellus on kokonaisuudessaan nähtävissä GitHubissa. Alla ruutukaappaus sovelluksen web-käyttöliittymästä:

Sääsovelluksen web-käyttöliittymä

Säätietojen lataaminen taustalla

Niin ylpeä kuin tuosta osissa lähetettävästä HTML-dokumentista olenkin, olisi käyttökokemus entistä mukavampi, jos sovelluksen välimuistia päivitettäisiin asynkronisesti taustalla. Silloin kävijän ei tarvitsisi koskaan odotella Open Weatherin rajapinnan vastauksia, vaan tiedot löytyisivät aina suoraan välimuistista. Tämän toteuttamiseksi tarvitaan jokin keino ajastaa päivitykset toimimaan tunnin välein. Sellainen onneksi löytyy Applen käyttöjärjestelmistä suoraan: launchd.

launchd on melko monimutkainen ja -käyttöinen systeemi, enkä ole koskaan perehtynyt sen sielunelämään kummemmin, mutta yksinkertaisten ajastettujen tehtävien luominen sille on kohtuullisen suoraviivaista. Tarvitaan vain XML-muotoinen plist-tiedosto, joka kuvailee tehtävän. Tiedosto näyttää tältä:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>fi.karhuhelsinki.load-weather</string>
    <key>Program</key>
    <string>/var/www/weather/scripts/load-weather.php</string>
    <key>UserName</key>
    <string>daemon</string>
    <key>StartInterval</key>
    <integer>3600</integer>
</dict>
</plist>

Jokaisella launchd-tehtävällä on oltava yksilöivä nimi, joka tässä tapauksessa on fi.karhuhelsinki.load-weather. launchd-tehtävien hallinnassa tehtäviin viitataan tällä nimellä, joten tehtävät kannattaa nimetä selkeästi. Ajettava ohjelma määritellään Program-merkkijonolla. Tässä tapauksessa ohjelmalle ei ole tarpeen välittää argumentteja, mutta jos niitä olisi, ne määriteltäisiin ProgramArguments-taulukossa. UserName kertoo, millä käyttäjällä ohjelma ajetaan ja StartInterval puolestaan suoritusintervallin sekunteina.

Kun plist-tiedosto on valmis, se ladataan launchd:lle käyttöön launchctl-työkalulla komennolla launchctl load /var/www/weather/launchd/load-weather.plist. Tämän jälkeen säätietojen pitäisi päivittyä itsekseen aina tunnin välein.

Koodin päivittäminen iPodille

Varsin aikaisessa vaiheessa projektia tajusin, että tiedostojen siirtäminen iPodille SSH:lla kävisi melko raskaaksi täysin manuaalisesti tehtynä. Kuten kaikessa muussakin, myös tässä mentiin sen yksinkertaisimman mallin mukaan:

  1. Vein julkisen SSH-avaimeni iPodille ~/.ssh/autohorized_keys-tiedostoon. Tämä eliminoi tarpeen antaa salasana aina uudestaan jokaisen SSH-yhteyden muodostamisen yhteydessä.
  2. Tein hyvin yksinkertaisen bash-skriptin, joka kopioi kaikki PHP-sovelluksen tiedostot iPodille SCP:llä, tyhjentää välimuistin sekä lataa launchd-tehtävän uusiksi.

Tämän jälkeen muutosten vienti iPodille toimii helposti vain ajamalla ym. bash-skripti. Mitään kovin korkean tason automatiikkaa tähän ei ole tarkoitus viritellä, koska tavoitteena on, että kehitysvaiheen jälkeen koodiin ei juurikaan olisi tarvetta koskea.

Lisäpähkäilyä ja tulevaisuudensuunnitelmia

Yksinkertaisten PHP-sovellusten ajaminen iPodilla on hienoa ja jopa hyödyllistä, mutta en haluaisi pysähtyä siihen. Tavoitteenani olisi kehittää natiivisti iPodilla ajettavia komentoriviohjelmia. Tähän tarkoitukseen olen mallaillut Rust-ohjelmointikieltä sekä sen varsin kattavia ristiinkääntöominaisuuksia. Uusimmat Rust-versiot ovat toki tiputtaneet tuen tähän tarkoitukseen sopivalta käännöskohteelta, mutta versiosta 1.41 tuo vaadittu armv7-apple-ios-kohde vielä löytyy.

Ristiinkääntämistä varten tietysti tarvitaan tukea vähän muualtakin kuin vain Rustilta ja uskoakseni juuri tästä syystä en ole saanut hommaa vielä täysin toimimaan. Käännösympäristöni on tällä hetkellä tilassa, jossa kääntäjä tuottaa periaatteessa ajettavan binääritiedoston, mutta sen ajaminen iOS:ssä tuottaa virheen Illegal instruction: 4. Ilmeisesti ongelmana on se, että käytössä oleva C-kääntäjä tuottaa ko. käyttöjärjestelmälle (iOS 5.1.1) soveltumatonta tauhkaa, mutta ongelman korjaaminen Rustin kanssa vaikuttaa varsin hankalalta. Päivitän tätä blogikirjoitusta, jos saan ristiinkääntämisen jotenkin onnistumaan.

Yllämainittu ristiinkääntäminen ei vieläkään toimi, enkä jaksa sen kanssa ehkä enempää tapellakaan. Sen sijaan sääasemaprojekti otti ison harppauksen eteenpäin, kun sain toimituksen e-radionicalta. Lisätietoa aiheesta on tulossa seuraavassa blogikirjoituksessani.