{"id":181,"date":"2014-01-01T11:00:00","date_gmt":"2014-01-01T00:00:00","guid":{"rendered":"https:\/\/www.icemoonprison.com\/blog\/?p=181"},"modified":"2014-01-01T12:43:03","modified_gmt":"2014-01-01T01:43:03","slug":"speak-the-weather","status":"publish","type":"post","link":"https:\/\/www.icemoonprison.com\/blog\/archives\/181","title":{"rendered":"Speak the weather"},"content":{"rendered":"<p>Here at Ice Moon Prison, going outside with insufficient clothing can be fatal. Hence we find ourselves checking the Bureau of Meteorology&#8217;s forecast often, even before we get up in the morning. Fumbling for a smartphone while still in bed is out of the question\u2014LCD screens are off-limits in sleeping quarters\u2014so we are experimenting with audio announcements.<br \/>\n<!--more--><br \/>\nThe BOM publishes all of its forecasts as <a title=\"Bureau of Meteorology data feeds\" href=\"http:\/\/www.bom.gov.au\/catalogue\/data-feeds.shtml\">easy-to-process XML documents<\/a>, which it pushes to its <a title=\"Bureau of Meteorology FTP site\" href=\"https:\/\/www.icemoonprison.com\/blog\/?p=175\">anonymous FTP server<\/a> twice a day.<\/p>\n<pre># Crontab entry to fetch the BOM XML for Melbourne metropolitan region.\r\nBOM_PRODUCT=IDV10450\r\n37 1,5,7,9,12,15,17,21 * * * \/usr\/bin\/curl -s -S ftp:\/\/ftp2.bom.gov.au\/anon\/gen\/fwo\/$BOM_PRODUCT.xml &gt; \/var\/speak-weather\/$BOM_PRODUCT.xml.new &amp;&amp; mv \/var\/speak-weather\/$BOM_PRODUCT.xml.new \/var\/speak-weather\/$BOM_PRODUCT.xml<\/pre>\n<p>Transforming one of these to a string of English text is a simple matter of <a title=\"Extensible Stylesheet Language (Transforms)\" href=\"https:\/\/en.wikipedia.org\/wiki\/XSLT\">XSLT<\/a>.\u00a0 Ice Moon Prison uses and recommends <a title=\"Saxon on Sourceforge\" href=\"http:\/\/saxon.sourceforge.net\/\">Saxon Home Edition<\/a>.<\/p>\n<pre>&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;!-- speak-weather.xsl --&gt;\r\n&lt;xsl:stylesheet xmlns:xsl=\"http:\/\/www.w3.org\/1999\/XSL\/Transform\" version=\"2.0\" xmlns:xs=\"http:\/\/www.w3.org\/2001\/XMLSchema\"&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;!-- Location to get temperature forecast for. --&gt;\r\n\u00a0\u00a0\u00a0 &lt;xsl:param name=\"aacTemperature\"\/&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;!-- Location to get precipitation forecast for. --&gt;\r\n\u00a0\u00a0\u00a0 &lt;xsl:param name=\"aacRain\"\/&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;!-- Time to get current conditions for. --&gt;\r\n\u00a0\u00a0\u00a0 &lt;xsl:param name=\"conditionTime\" select=\"current-dateTime()\"\/&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;!-- Time to get forecast for. --&gt;\r\n\u00a0\u00a0\u00a0 &lt;xsl:param name=\"forecastTime\" select=\"current-dateTime() + xs:dayTimeDuration('PT18H')\"\/&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:output method=\"text\"\/&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;!-- \"Tonight\" forecast (from 5 pm to midnight). --&gt;\r\n\u00a0\u00a0\u00a0 &lt;xsl:template name=\"tonight\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;!-- Current conditions forecast --&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"\/product\/forecast\/area[@aac=$aacTemperature]\" mode=\"tonight\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:with-param name=\"time\" select=\"$conditionTime\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:apply-templates&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt; &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"\/product\/forecast\/area[@aac=$aacTemperature]\" mode=\"tomorrow-minimum\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:with-param name=\"time\" select=\"$forecastTime\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:apply-templates&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt; &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;!-- Tomorrow's conditions forecast. --&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"\/product\/forecast\/area[@aac=$aacTemperature]\" mode=\"tomorrow\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:with-param name=\"time\" select=\"$forecastTime\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:apply-templates&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt; &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"\/product\/forecast\/area[@aac=$aacTemperature]\" mode=\"tomorrow-maximum\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:with-param name=\"time\" select=\"$forecastTime\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:apply-templates&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;!-- \"Today\" forecast (from 5 am to 5 pm). --&gt;\r\n\u00a0\u00a0\u00a0 &lt;xsl:template name=\"today\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;!-- Today's conditions forecast. --&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"\/product\/forecast\/area[@aac=$aacTemperature]\" mode=\"today\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:with-param name=\"time\" select=\"$conditionTime\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:apply-templates&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt; &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"\/product\/forecast\/area[@aac=$aacTemperature]\" mode=\"today-maximum\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:with-param name=\"time\" select=\"$conditionTime\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:apply-templates&gt;\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt; &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"\/product\/forecast\/area[@aac=$aacRain]\" mode=\"today-rain\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:with-param name=\"time\" select=\"$conditionTime\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:apply-templates&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;!-- Recurse throuch all parent AACs to get local, district, state levels. --&gt;\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"area\" mode=\"#all\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:param name=\"time\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"forecast-period[xs:dateTime(@start-time-utc) le $time][xs:dateTime(@end-time-utc) gt $time]\" mode=\"#current\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:sort select=\"@index\" data-type=\"number\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:apply-templates&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"\/product\/forecast\/area[@aac=current()\/@parent-aac]\" mode=\"#current\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:with-param name=\"time\" select=\"$time\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:apply-templates&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;!--\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Select parts of each forecast-period for converting into text.\r\n\u00a0\u00a0\u00a0 --&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\" mode=\"tonight\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"text[@type='forecast']|text[@type='precis']\" mode=\"tonight\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\" mode=\"tomorrow-minimum\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"element[@type='air_temperature_minimum']\" mode=\"tomorrow-minimum\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\" mode=\"tomorrow\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"text[@type='forecast']|text[@type='precis']\" mode=\"tomorrow\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\" mode=\"tomorrow-maximum\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"element[@type='air_temperature_maximum']\" mode=\"tomorrow-maximum\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\" mode=\"today\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"text[@type='forecast']|text[@type='precis']\" mode=\"today\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\" mode=\"today-maximum\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"element[@type='air_temperature_maximum']\" mode=\"today-maximum\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\" mode=\"today-rain\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:if test=\"text[@type='probability_of_precipitation'] ne '0%' and element[@type='precipitation_range']\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt;Chance of rain: &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"text[@type='probability_of_precipitation']\" mode=\"today-rain\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt; of &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\"element[@type='precipitation_range']\" mode=\"today-rain\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt;.&lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:if&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;!--\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Turn elements into text.\r\n\u00a0\u00a0\u00a0 --&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\/text[@type='forecast']|forecast-period\/text[@type='precis']\" mode=\"tonight\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt;For the rest of today: &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\".\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\/text[@type='forecast']|forecast-period\/text[@type='precis']\" mode=\"tomorrow\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt;Tomorrow: &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\".\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\/text[@type='forecast']|forecast-period\/text[@type='precis']\" mode=\"today\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt;Today: &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:apply-templates select=\".\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\/element[@type='air_temperature_minimum']\" mode=\"tomorrow-minimum\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt;Tonight, down to &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:value-of select=\"text()\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt;.&lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\/element[@type='air_temperature_maximum']\" mode=\"tomorrow-maximum today-maximum\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt;Maximum: &lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:value-of select=\"text()\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt;.&lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\/text[@type='probability_of_precipitation']\" mode=\"today-rain\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:value-of select=\"text()\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"forecast-period\/element[@type='precipitation_range']\" mode=\"today-rain\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:choose&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:when test=\"text() = '0 mm'\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:text&gt;a trace&lt;\/xsl:text&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:when&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:otherwise&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;xsl:value-of select=\"text()\"\/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:otherwise&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/xsl:choose&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/xsl:template&gt;\r\n\r\n\u00a0\u00a0\u00a0 &lt;!-- Not interesting. Omit. --&gt;\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"text[@type='warning-summary-footer']\"\/&gt;\r\n\u00a0\u00a0\u00a0 &lt;xsl:template match=\"text[@type='product-footer']\"\/&gt;\r\n\r\n&lt;\/xsl:stylesheet&gt;<\/pre>\n<pre># Crontab entry to transform stylesheet into English text.\r\nAAC_TEMPERATURE=VIC_PT065\r\nAAC_RAIN=VIC_PT042\r\n38 0-4,17-23 * * * \/usr\/bin\/java -cp \/usr\/local\/lib\/saxon9he.jar net.sf.saxon.Transform -it:tonight -xsl:$HOME\/lib\/speak-weather.xsl -s:\/var\/speak-weather\/$BOM_PRODUCT.xml aacTemperature=$AAC_TEMPERATURE aacRain=$AAC_RAIN &gt; \/var\/speak-weather\/scoresby.txt.new &amp;&amp; mv \/var\/speak-weather\/scoresby.txt.new \/var\/speak-weather\/scoresby.txt\r\n38 5-16 * * * \/usr\/bin\/java -cp \/usr\/local\/lib\/saxon9he.jar net.sf.saxon.Transform -it:today -xsl:$HOME\/lib\/speak-weather.xsl -s:\/var\/speak-weather\/$BOM_PRODUCT.xml aacTemperature=$AAC_TEMPERATURE aacRain=$AAC_RAIN &gt; \/var\/speak-weather\/scoresby.txt.new &amp;&amp; mv \/var\/speak-weather\/scoresby.txt.new \/var\/speak-weather\/scoresby.tx<\/pre>\n<p>Text-to-speech has improved by leaps and bounds in recent years, to the point where Ice Moon Prison has freed its now-redundant radio announcers (except the ones who say &#8220;nucular&#8221;). A Mac Mini running OS X Mountain Lion can &#8220;say&#8221; the forecast in a number of realistic accents and produce an audio file that can be stored on a web server.<\/p>\n<pre># Crontab entry to produce an AAC file of the forecast.\r\n39 * * * * (echo \"[[slnc 50]]\" &amp;&amp; sed 's\/\\([Ww]\\)inds\/\\1innds\/g;s\/\\([Ss]\\)eabreeze\/\\1ea breeze\/g' \/var\/speak-weather\/scoresby.txt ) | \/usr\/bin\/say -v Lee -o \/var\/speak-weather\/scoresby.m4a -f - &amp;&amp; cp \/var\/speak-weather\/scoresby.m4a \/Library\/Server\/Web\/Data\/Sites\/Default\/speak-weather\/scoresby.m4a<\/pre>\n<p>(Some words are mispronounced by Lee, so a little search-and-replace is in order to prevent &#8220;winds&#8221; rhyming with &#8220;kinds&#8221;.\u00a0 Inserting a short silence at the beginning ensures that the first word is not clipped on playback.)<\/p>\n<p>Here is a forecast from January 1 2014 as an example: <audio class=\"wp-audio-shortcode\" id=\"audio-181-1\" preload=\"none\" style=\"width: 100%;\" controls=\"controls\"><source type=\"audio\/mpeg\" src=\"https:\/\/www.icemoonprison.com\/blog\/wp-content\/uploads\/2014\/01\/scoresby.m4a?_=1\" \/><a href=\"https:\/\/www.icemoonprison.com\/blog\/wp-content\/uploads\/2014\/01\/scoresby.m4a\">https:\/\/www.icemoonprison.com\/blog\/wp-content\/uploads\/2014\/01\/scoresby.m4a<\/a><\/audio><\/p>\n<p>Announcing the forecast in the sleeping quarters is trivially easy with a <a title=\"Sonos\" href=\"http:\/\/www.sonos.com\/\">Sonos<\/a> Internet-connected speaker and a <a title=\"Vera (MiCasaVerde)\" href=\"http:\/\/getvera.com\/\">Vera<\/a> home automation gateway. A scene is activated by a button press on a remote control, <a title=\"Sonos plugin for Vera\" href=\"http:\/\/code.mios.com\/trac\/mios_sonos-wireless-music-systems\">instructing the Sonos<\/a> to play the most recent audio file.<\/p>\n<figure id=\"attachment_184\" aria-describedby=\"caption-attachment-184\" style=\"width: 625px\" class=\"wp-caption alignleft\"><a href=\"https:\/\/www.icemoonprison.com\/blog\/wp-content\/uploads\/2014\/01\/Sonos-Vera-scene.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-184\" alt=\"Vera scene editor\" src=\"https:\/\/www.icemoonprison.com\/blog\/wp-content\/uploads\/2014\/01\/Sonos-Vera-scene-1024x483.png\" width=\"625\" height=\"294\" srcset=\"https:\/\/www.icemoonprison.com\/blog\/wp-content\/uploads\/2014\/01\/Sonos-Vera-scene-1024x483.png 1024w, https:\/\/www.icemoonprison.com\/blog\/wp-content\/uploads\/2014\/01\/Sonos-Vera-scene-300x141.png 300w, https:\/\/www.icemoonprison.com\/blog\/wp-content\/uploads\/2014\/01\/Sonos-Vera-scene-624x294.png 624w, https:\/\/www.icemoonprison.com\/blog\/wp-content\/uploads\/2014\/01\/Sonos-Vera-scene.png 1027w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><figcaption id=\"caption-attachment-184\" class=\"wp-caption-text\">Advanced tab of Vera Scene editor, showing HTTP URL of forecast AAC (m4a) file.<\/figcaption><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Here at Ice Moon Prison, going outside with insufficient clothing can be fatal. Hence we find ourselves checking the Bureau of Meteorology&#8217;s forecast often, even before we get up in the morning. Fumbling for a smartphone while still in bed is out of the question\u2014LCD screens are off-limits in sleeping quarters\u2014so we are experimenting with [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":3,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":""},"categories":[44,45,19],"tags":[48,47,46],"class_list":["post-181","post","type-post","status-publish","format-standard","hentry","category-automation","category-weather","category-xml","tag-bureau-of-meteorology","tag-text-to-speech","tag-xslt"],"_links":{"self":[{"href":"https:\/\/www.icemoonprison.com\/blog\/wp-json\/wp\/v2\/posts\/181","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.icemoonprison.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.icemoonprison.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.icemoonprison.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.icemoonprison.com\/blog\/wp-json\/wp\/v2\/comments?post=181"}],"version-history":[{"count":4,"href":"https:\/\/www.icemoonprison.com\/blog\/wp-json\/wp\/v2\/posts\/181\/revisions"}],"predecessor-version":[{"id":187,"href":"https:\/\/www.icemoonprison.com\/blog\/wp-json\/wp\/v2\/posts\/181\/revisions\/187"}],"wp:attachment":[{"href":"https:\/\/www.icemoonprison.com\/blog\/wp-json\/wp\/v2\/media?parent=181"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.icemoonprison.com\/blog\/wp-json\/wp\/v2\/categories?post=181"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.icemoonprison.com\/blog\/wp-json\/wp\/v2\/tags?post=181"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}