From ca03a5ce635f09e96749761bb2da951b2160d8dc Mon Sep 17 00:00:00 2001 From: jmike <> Date: Mon, 19 Dec 2016 20:59:43 +0000 Subject: [PATCH] 98_TRAFFIC: new feature, map, return waypoint, v1.2 git-svn-id: https://svn.fhem.de/fhem/trunk@12838 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/98_TRAFFIC.pm | 145 +++++++++++++++++++++++++++++++++++----- 2 files changed, 131 insertions(+), 15 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index ba5ed5164..f25a0e9a9 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: 98_TRAFFIC: reverse waypoints, integrated map for visualization - update: 77_UWZ: New Version 1.4.7 added headlines for weblinks - update: 73_NUKIBridge,74_NUKIDevice: New Version 0.4.0 More Feature get,set ,Callback/Webhook Funktion diff --git a/fhem/FHEM/98_TRAFFIC.pm b/fhem/FHEM/98_TRAFFIC.pm index d9d141a01..aa0dc7918 100644 --- a/fhem/FHEM/98_TRAFFIC.pm +++ b/fhem/FHEM/98_TRAFFIC.pm @@ -17,6 +17,13 @@ # You should have received a copy of the GNU General Public License # along with fhem. If not, see . # +# versioning: MAJOR.MINOR.PATCH, increment the: +# MAJOR version when you make incompatible API changes +# - includes changing CLI options, changing log-messages +# MINOR version when you add functionality in a backwards-compatible manner +# - includes adding new features and log-messages (as long as they don't break anything existing) +# PATCH version when you make backwards-compatible bug fixes. +# ############################################################################## # Changelog: # @@ -30,18 +37,21 @@ # 2016-10-07 version 1.0, adding to SVN # 2016-10-15 adding attribute updateSchedule to provide flexible updates, changed internal interval to INTERVAL # 2016-12-13 adding travelMode, fixing stateReading with value 0 +# 2016-12-15 adding reverseWaypoints attribute, adding weblink with auto create route via gmaps on verbose 5 package main; use strict; use warnings; -use Time::HiRes qw(gettimeofday); use Data::Dumper; +use Time::HiRes qw(gettimeofday); use LWP::Simple qw($ua get); -use JSON; -use POSIX; use Blocking; +use POSIX; +die "MIME::Base64 missing!" unless(eval{require MIME::Base64}); +die "JSON missing!" unless(eval{require JSON}); + sub TRAFFIC_Initialize($); sub TRAFFIC_Define($$); @@ -53,7 +63,7 @@ sub TRAFFIC_GetUpdate($); my %TRcmds = ( 'update' => 'noArg', ); -my $TRVersion = '1.1'; +my $TRVersion = '1.2'; sub TRAFFIC_Initialize($){ @@ -64,9 +74,11 @@ sub TRAFFIC_Initialize($){ $hash->{SetFn} = "TRAFFIC_Set"; $hash->{AttrFn} = "TRAFFIC_Attr"; $hash->{AttrList} = - "disable:0,1 start_address end_address raw_data:0,1 language waypoints stateReading outputReadings travelMode:driving,walking,bicycling,transit includeReturn:0,1 updateSchedule " . + "disable:0,1 start_address end_address raw_data:0,1 language waypoints returnWaypoints stateReading outputReadings travelMode:driving,walking,bicycling,transit includeReturn:0,1 updateSchedule " . $readingFnAttributes; - + $data{FWEXT}{"/TRAFFIC"}{FUNC} = "TRAFFIC_debug"; + $data{FWEXT}{"/TRAFFIC"}{FORKABLE} = 1; + } sub TRAFFIC_Define($$){ @@ -85,7 +97,6 @@ sub TRAFFIC_Define($$){ $hash->{VERSION} = $TRVersion; delete($hash->{BURSTCOUNT}) if $hash->{BURSTCOUNT}; delete($hash->{BURSTINTERVAL}) if $hash->{BURSTINTERVAL}; - my $name = $hash->{NAME}; @@ -145,7 +156,19 @@ sub TRAFFIC_Attr(@){ if($attrName eq "disable" && $attrValue eq "1"){ readingsSingleUpdate( $hash, "state", "disabled", 1 ); } - if($attrName eq "outputReadings" || $attrName eq "includeReturn"){ + + if($attrName eq "verbose" && $attrValue eq "5"){ + if (!defined $defs{$name."_weblink"}) { + FW_fC("define ".$name."_weblink weblink htmlCode {TRAFFIC_weblink(\"".$name."\",0)}"); + FW_fC("attr ".$name."_weblink room TRAFFIC_debug"); + Log3 $hash, 5, "TRAFFIC: ($name) weblink created"; + } + }elsif($attrName eq "verbose" && $attrValue < 5){ + FW_fC("delete ".$name."_weblink"); + } + + + if($attrName eq "outputReadings" || $attrName eq "includeReturn" || $attrName eq "verbose"){ #clear all readings foreach my $clearReading ( keys %{$hash->{READINGS}}){ Log3 $hash, 5, "TRAFFIC: ($name) READING: $clearReading deleted"; @@ -201,7 +224,10 @@ sub TRAFFIC_Set($@){ InternalTimer($updateTrigger, "TRAFFIC_StartUpdate", $hash, 0); return undef; + }elsif($set =~ m/debug/){ + # TRAFFIC_widget(); } + } @@ -325,13 +351,19 @@ sub TRAFFIC_DoUpdate(){ my $TRwaypoints = ''; if(defined(AttrVal($name,"waypoints",undef))){ $TRwaypoints = '&waypoints=via:' . join('|via:', split('\|', AttrVal($name,"waypoints",undef))); - - if($direction eq "return"){ - $TRwaypoints = '&waypoints=via:' . join('|via:', reverse split('\|', AttrVal($name,"waypoints",undef))); - Log3 $hash, 5, "TRAFFIC: ($name) reversing waypoints"; - } }else{ - Log3 $hash, 5, "TRAFFIC: ($name) no waypoints specified"; + Log3 $hash, 3, "TRAFFIC: ($name) no waypoints specified"; + } + if($direction eq "return"){ + if(defined(AttrVal($name,"returnWaypoints",undef))){ + $TRwaypoints = '&waypoints=via:' . join('|via:', split('\|', AttrVal($name,"returnWaypoints",undef))); + Log3 $hash, 3, "TRAFFIC: ($name) using returnWaypoints"; + }elsif(defined(AttrVal($name,"waypoints",undef))){ + $TRwaypoints = '&waypoints=via:' . join('|via:', reverse split('\|', AttrVal($name,"waypoints",undef))); + Log3 $hash, 3, "TRAFFIC: ($name) reversing waypoints"; + }else{ + Log3 $hash, 3, "TRAFFIC: ($name) no waypoints for return specified"; + } } my $origin = AttrVal($name, "start_address", 0 ); @@ -361,6 +393,9 @@ sub TRAFFIC_DoUpdate(){ $returnJSON->{'status'} = $json->{'status'}; $returnJSON->{'eta'} = FmtTime( gettimeofday() + $duration_in_traffic_sec ) if defined($duration_in_traffic_sec); + $returnJSON->{'debugLocation'} = $json->{'routes'}[0]->{'legs'}[0]->{start_location}->{lat}.','.$json->{'routes'}[0]->{'legs'}[0]->{start_location}->{lng} if AttrVal($name, "verbose", 0 ) == 5; + $returnJSON->{'debugPoly'} = encode_base64 ($json->{'routes'}[0]->{overview_polyline}->{points}) if AttrVal($name, "verbose", 0 ) == 5; + if($duration_in_traffic_sec && $duration_sec){ $returnJSON->{'delay'} = prettySeconds($duration_in_traffic_sec - $duration_sec) if AttrVal($name, "outputReadings", "" ) =~ m/text/; Log3 $hash, 3, "TRAFFIC: ($name) delay in seconds = $duration_in_traffic_sec - $duration_sec"; @@ -445,11 +480,86 @@ sub TRAFFIC_FinishUpdate($){ Log3 $hash, 1, "TRAFFIC: ($name) stateReading $stateReading not found"; } } - readingsEndUpdate($hash, $dotrigger); Log3 $hash, 3, "TRAFFIC: ($name) TRAFFIC_FinishUpdate done"; } +sub TRAFFIC_weblink{ + my $name = shift(); + my $return = shift(); + return "open Map for TRAFFIC $name
"; +} + +sub TRAFFIC_debug(){ + my $name = $FW_webArgs{name}; + my $return = $FW_webArgs{return}; + return if(!defined($name)); + + $FW_RETTYPE = "text/html; charset=UTF-8"; + $FW_RET=""; + + Log 1,"[traffic debug] called for $name"; + + my $debugPoly = 'debugPoly'; + my $debugLocation = 'debugLocation'; + if($return eq 1){ + $debugPoly = 'return_'.$debugPoly; + $debugLocation = 'return_'.$debugLocation; + } + + my $web = ' +'; +$web .= '' if decode_base64(ReadingsVal($name, "return_debugPoly", undef) ); +$web .= ' +
+ +'; + FW_pO $web; + return ($FW_RETTYPE, $FW_RET); +} sub prettySeconds { my $time = shift; @@ -502,6 +612,7 @@ sub prettySeconds { requirements:
perl JSON module
perl LWP::SIMPLE module
+ perl MIME::Base64 module
Google maps API key

Features: @@ -523,6 +634,9 @@ sub prettySeconds {
  • configure the state-reading
  • optionally display the same route in return
  • one-time-burst, specify the amount and interval between updates
  • +
  • different Travel Modes (driving, walking, bicycling and transit)
  • +
  • flexible update schedule
  • +
  • integrated Map to visualize configured route at verbose 5


  • @@ -543,6 +657,7 @@ sub prettySeconds {
  • "raw_data" - 0:1
  • "language" - de, en etc.
  • "waypoints" - Lat, Long coordinates, separated by |
  • +
  • "returnWaypoints" - Lat, Long coordinates, separated by |
  • "disable" - 0:1
  • "stateReading" - name the reading which will be used in device state
  • "outputReadings" - define what kind of readings you want to get: text, min, sec, average