📜 ⬆️ ⬇️

Variation on the subject: weather forecast by phone

I decided to share another way to organize the service of weather forecasting by phone. Here, in comparison with this post , more intelligence is transferred to Asterisk.
Weather


Weather xml


I take XML with the current weather and forecast for the next two days from the BBC.

Since we cannot get rid of Cron, we add:
0 2 * * * /home/alexandr/xml/weather/almaty/bbcweather.sh
0 14 * * * /home/alexandr/xml/weather/almaty/bbcweather.sh
30 16 * * * /home/alexandr/xml/weather/almaty/bbcweather.sh

It was experimentally established that XML is updated three times a day (at least for the city of Almaty).

The script not only downloads, but also processes the XML file and decomposes the necessary values ​​into files. True, my script is very primitive and most likely it will embarrass many with its inefficiency, so I will not focus on it.
')

Text files


File names for the same parameter (for example, the minimum temperature) for different days will differ in the prefix number: 1 - today, 2 and 3 - tomorrow and day after tomorrow, respectively. For example, the minimum temperature for today (the first data block in XML) will be stored in a file called “1Tmin”, for tomorrow (the second data block) - “2Tmin”.

Now on the contents of the files (which, by the way, should not contain the end of line character):

For testing (and also for writing a script) I used the "-n" parameter of the echo command in order not to write the line end character to the file:
echo -n "day-5" > 1Wday

So, we have a list of files with weather data. Now go to the configuration extensions.conf.

extensions.conf


 [weather] exten => 071,1,Set(wpath="/home/your-directory/") exten => 071,n,Goto(say-weather,s,1) [say-weather] exten => s,1,Answer() exten => s,n,Set(E=1) exten => s,n(play),Playback(digits/${SHELL(cat ${wpath}${E}Wday)}) exten => s,n,Playback(custom/temperatura) exten => s,n,SayNumber(${SHELL(cat ${wpath}${E}Tmin)}) exten => s,n,SayNumber(${SHELL(cat ${wpath}${E}Tmax)}) exten => s,n,Playback(custom/wind) exten => s,n,Playback(custom/wind/${SHELL(cat ${wpath}${E}Wdir)}) exten => s,n,SayNumber(${SHELL(cat ${wpath}${E}Wspd)}) exten => s,n,Playback(custom/metrov-v-sekundu) exten => s,n,Playback(custom/vlazhnost) exten => s,n,SayNumber(${SHELL(cat ${wpath}${E}Humd)}) exten => s,n,Playback(custom/procentov) exten => s,n,WaitExten() exten => s,n,Hangup() exten => _[1-3],1,Set(E=${EXTEN}) exten => _[1-3],n,Goto(say-weather,s,play) 


Debriefing

We publish an extension, by which we will dial up to the weather and, at the same time, set the “wpath” variable, where we specify the directory of previously created files:
 exten => 071,1,Set(wpath="/home/your-directory/") 

Go to the context of say-weather:
 exten => 071,n,Goto(say-weather,s,1) 

After initializing the value “E” (by default, we set today), we reproduce the name of the day of the week:
 exten => s,n,Set(E=1) exten => s,n(play),Playback(digits/${SHELL(cat ${wpath}${E}Wday)}) 

Playback team can not be represented, but then just in case I will tell. If a relative path is specified, the default (in any case on my debian) is "/ usr / share / asterisk / sounds / en /" (or "ru /", if the "language = ru" parameter is specified in sip.conf) . We have already defined the values ​​of “wpath” and “E”, therefore the following is obtained:
 Playback(/usr/share/asterisk/sounds/ru/digits/${SHELL(cat /home/your-directory/1Wday)}) 

Let the result of executing "cat / home / your-directory / 1Wday" be, for example, the string "day-0" (suppose it is Sunday today), then:
 Playback(/usr/share/asterisk/sounds/ru/digits/day-0) 

Those. the file containing the name of the day of the week we need is played because The file digits / day-0 just contains the sound recording "Sunday".

This line also works on the same principle:
 SayNumber(${SHELL(cat ${wpath}${E}Tmin)}) 

The minimum temperature of today is pronounced, the value of which is taken from the file "/ home / your-directory / 1Tmin".

Alas, I had to write some messages on my own; I placed them in the “custom” directory.
 exten => s,n,Playback(custom/temperatura) 

After the end of all Playback and SayNumber, we are waiting for the introduction of additional numbers:
 exten => s,n,WaitExten() 

Additional numbers include 1, 2 and 3, as can be seen from the pattern _ [1-3]:
 exten => _[1-3],1,Set(E=${EXTEN}) exten => _[1-3],n,Goto(say-weather,s,play) 

When you click on any of them, the first line assigns the entered value to the variable “E”, and the second line jumps to the label “play” (the third line of this context). Thus, Asterisk will again inform the calling weather, but already for tomorrow (if the number 2 was entered) or the day after (3).

Nothing prevents us from writing another extension next to 071 and using the same context of say-weather to reproduce the weather for another city:
 [weather] exten => 071,1,Set(wpath="/home/user/weather/city1/files") exten => 071,n,Goto(say-weather,s,1) exten => 072,1,Set(wpath="/home/user/weather/city2/files") exten => 072,n,Goto(say-weather,s,1) 


From where I downloaded the archive with Russian messages I don’t remember, but it should be easy to google.
Detailed descriptions of Asterisk features at www.voip-info.org .

Bonus


As a bonus, I post part of extensions.conf, with which I recorded my audio messages:
 [test-context] exten => 051,1,Goto(rec-file,s,1) [rec-file] exten => s,1,Verbose(1,Recording application) exten => s,n,Answer() exten => s,n,Playback(record-enter-num) exten => s,n(filename),Read(filename) exten => s,n(rec-mes),Record(/tmp/recs/${filename}:gsm) exten => s,n,Playback(beep) exten => s,n,Wait(1) exten => s,n(rec-play),Playback(/tmp/recs/${filename}) exten => s,n(rec-review),Background(vm-review) exten => s,n,WaitExten(10) exten => s,n(rec-del),System(rm /tmp/recs/${filename}.gsm) exten => s,n,Playback(vm-deleted) exten => s,n(read-hang),Read(rep,,1,,,2);Wait for 1 digit for 2 seconds to receive 1 which means that another file is to be recorded exten => s,n,Gotoif($["${rep}" = "1"]?filename) exten => s,n,Hangup() exten => 1,1,Playback(vm-msgsaved) exten => 1,n,SayDigits(${filename}) exten => 1,n,Goto(rec-file,s,read-hang) exten => 2,1,Goto(rec-file,s,rec-play) exten => 3,1,Wait(1) exten => 3,n,Goto(rec-file,s,rec-mes) exten => i,1,Goto(rec-file,s,rec-review) exten => t,1,Goto(rec-file,s,rec-del) exten => 0,1,Goto(rec-file,s,rec-del) 

We are preparing a sign with a list of file names (in the form of numbers) and text that needs to be spoken, we call from a more or less good phone / headset to 051, “run” each line of our tablet and at the end pick up the finished files from / tmp / recs / (not forgetting to change their names to something more understandable).

And a hint to make it easier to use:

Recording Menu


UPD: Link to bbcweather.sh: pastebin.com/DJYy6XRM and sed.bbc: pastebin.com/Xm4EeDst . If anyone does with a normal XML parser, do not forget to share the code :-).

UPD2: I post the rewritten script (this time on Perl) and extract from extensions.conf:

weather.sh


 #!/usr/bin/perl use XML::LibXML; my $d = "/home/alexandr/xml/perl/bbc/moscow/"; # Working directory my $f = "files/"; # Directory with weather files my $wget = "wget -qO - http://newsrss.bbc.co.uk/weather/forecast/58/Next3DaysRSS.xml > "; my $filename = "bbc.xml"; # XML-file to process my $i = 3; # Counter: try to download XML-file $i time(s) my $wait = 2; # Wait for $wait second(s) between downloads my $log = "/var/log/bbc_weather_moscow.log"; # Log-file :-) (used for logging failed download attempts) my %days = ( "Sunday" => "day-0", "Monday" => "day-1", "Tuesday" => "day-2", "Wednesday" => "day-3", "Thursday" => "day-4", "Friday" => "day-5", "Saturday" => "day-6"); sub t2f{ # Subroutine for writing text to file my($text, $file, $app) = @_; # Read parameters $text, $file (add non-zero $app to append, but not rewrite file) if ($app) { open (OUT_FILE, '>>', $file); } else { open (OUT_FILE, '>', $file); } print OUT_FILE "$text"; close (OUT_FILE); } sub noun{ # Choose noun for number my($number, $form1, $form2, $form5) = @_; # Usage: &noun(1, , , ); my $n10 = abs($number) % 10; my $n100 = abs($number) % 100; if ( $n100 > 4 && $n100 < 21 || $n10 == 0 || $n10 > 4 ) { return $form5 }; if ( $n10 > 1 && $n10 < 5 ) { return $form2 }; if ( $n10 == 1 ) { return $form1 }; return $form5; } chdir $d; # Go to working directory DOWNLOAD: # Label for 'Goto' (will be used in case XML has not been updated) $i -= 1; #print("Downloading...\n"); system($wget.$filename); my $parser = XML::LibXML->new(); my $doc = $parser->parse_file($filename); # Parse this file ($filename) #check if XML has been updated open(FILE, 'date.txt'); my $date = <FILE>; close(FILE); my @xmldate = $doc->findnodes('//pubDate'); if ( $date eq @xmldate[0]->to_literal ) { my $t = localtime; if ($i) { &t2f("$t: XML has not been updated, I will try $i more ".&noun($i,"time","times","times")."\n",$log,1); } else { &t2f("$t: XML has not been updated, I will not try any more\n",$log,1); exit; # No more tries left -> Finish myself! } sleep $wait; goto DOWNLOAD; # Tried to do with 'While' or "Do, while', but variables should be initialised outside loop, decided to use 'Goto' } else { &t2f(@xmldate[0]->to_literal,"date.txt"); } #searching for //item/title and //item/description values $i = 0; foreach my $item ($doc->findnodes('//item')) { # for each //item $i += 1; # BTW, we should be counting from 1 to 3 (today, tomorrow and day after tomorrow) my $title = $item->findnodes('./title'); $title = $title->to_literal; #take ./title and convert to string my $desc = $item->findnodes('./description'); $desc = $desc->to_literal; #take ./description and convert to string chdir $d.$f; # Go to directory with weather files if ( $title =~ m/^(\w+)/ ) { # Match first word (weekday) &t2f($days{"$1"},$i."Wday"); } # Export weekday in format day-0, day-1, etc if ( $title =~ m/: (\w+(\s\w+){0,}),/ ) { # Match one or more words, delimited with spaces (matched pattern should be followed by comma) &t2f("$1",$i."Prec"); } if ( $desc =~ m/Max Temp: (-{0,1}\d+)/ ) { &t2f("$1",$i."Tmax"); # Export Tmax (Max Temperature) &t2f(&noun("$1","gradus","gradusa","gradusov"),$i."DMax"); } # Export Dmax (right form of "" for Tmax) if ( $desc =~ m/Min Temp: (-{0,1}\d+)/ ) { &t2f("$1",$i."Tmin"); # Export Tmin (Min Temperature) &t2f(&noun("$1","gradus","gradusa","gradusov"),$i."DMin"); } if ( $desc =~ m/Wind Direction: (\w+)/ ) { # Export Wdir (Wind Direction) &t2f("$1",$i."Wdir"); } if ( $desc =~ m/Wind Speed: (\d+)/ ) { &t2f(int("$1"*0.44704+0.5),$i."Wspd"); # Export Wspd (mph*0.44704=kmh), int(kmh+0.5) - rounding to nearest integer &t2f(&noun("$1"*0.44704+0.5,"m_s","ma_s","mov_s"),$i."Wsms"); } # (,)   if ( $desc =~ m/Visibility: (\w+)/ ) { &t2f("$1",$i."Visb"); } # Export Visb (Visibility) if ( $desc =~ m/Pressure: (\d+)/ ) { &t2f(int("$1"*0.75),$i."Pres"); # Export Pres (Pressure) (mb*0.75=mmHg) &t2f(&noun("$1","mm_hg","mma_hg","mmov_hg"),$i."Prmm"); } # (,)   if ( $desc =~ m/Humidity: (\d+)/ ) { &t2f(int("$1"),$i."Humd"); # Export Humd (Humidity) &t2f(&noun("$1","procent","procenta","procentov"),$i."Hper"); } } 


extensions.conf


exten => 072,1,Set(wpath="/home/alexandr/xml/perl/bbc/moscow/files/")
exten => 072,n,System(echo "${STRFTIME(${EPOCH},,%Y.%m.%d %H:%M:%S)}\t${CALLERID(name)}\t${CALLERID(num)}" >> /var/log/calls_to_moscow_weather.log)
exten => 072,n,Goto(say-weather-perl,s,1)

[say-weather-perl]
exten => s,1,Answer()
exten => s,n,Set(E=1)
exten => s,n,Set(CHANNEL(language)=alex_01) ; By default use directory: /usr/share/asterisk/sounds/alex_01/
exten => s,n(play),Background(digits/${SHELL(cat ${wpath}${E}Wday)}) ; Weekday
exten => s,n,Background(weather/${SHELL(cat ${wpath}${E}Prec)}) ; Precipitation
exten => s,n,Background(weather/temperatura) ; Precipitation
exten => s,n,SayNumber(${SHELL(cat ${wpath}${E}Tmin)}) ; Minimum temperature
exten => s,n,SayNumber(${SHELL(cat ${wpath}${E}Tmax)}) ; Maximum temperature
exten => s,n,Background(weather/${SHELL(cat ${wpath}${E}DMax)}) ; Maximum degree(s)
exten => s,n,Background(weather/veter) ; Wind
exten => s,n,Background(weather/${SHELL(cat ${wpath}${E}Wdir)}) ; Wind Direction
exten => s,n,SayNumber(${SHELL(cat ${wpath}${E}Wspd)}) ; Wind speed
exten => s,n,Background(weather/${SHELL(cat ${wpath}${E}Wsms)}) ; Meter(s) per seconD
exten => s,n,Background(weather/vlazhnost) ; Humidity
exten => s,n,SayNumber(${SHELL(cat ${wpath}${E}Humd)}) ; Humidity percentage
exten => s,n,Background(weather/${SHELL(cat ${wpath}${E}Hper)}) ; Percent(s)

exten => s,n,WaitExten()
exten => s,n,Hangup()
exten => _[1-3],1,Set(E=${EXTEN})
exten => _[1-3],n,Goto(say-weather-perl,s,play)

UPD3: I give to the attention of the community about a free weather forecast service by phone for the city of Moscow. Weather forecast is available by phone +7 (499) 753-00-09. This number is kindly provided by Doc.Telecom , which is grateful for the assistance in providing this service to everyone. Thank :-)!

Source: https://habr.com/ru/post/139656/


All Articles