mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 18:59:33 +00:00
98_TRAFFIC: adding new module, version 1.0
git-svn-id: https://svn.fhem.de/fhem/trunk@12293 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
fbadb2219b
commit
f9ad4f5148
@ -1,6 +1,7 @@
|
|||||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
# 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.
|
# Do not insert empty lines here, update check depends on it.
|
||||||
|
|
||||||
|
- new: 98_TRAFFIC: provide traffic details with Google Distance API
|
||||||
- feature: 98_Hyperion: set configFile only available if at least two files
|
- feature: 98_Hyperion: set configFile only available if at least two files
|
||||||
are found while get configFiles
|
are found while get configFiles
|
||||||
minor code improvements
|
minor code improvements
|
||||||
|
531
fhem/FHEM/98_TRAFFIC.pm
Normal file
531
fhem/FHEM/98_TRAFFIC.pm
Normal file
@ -0,0 +1,531 @@
|
|||||||
|
#########################################################################
|
||||||
|
# $Id$
|
||||||
|
# fhem Modul which provides traffic details with Google Distance API
|
||||||
|
#
|
||||||
|
# This file is part of fhem.
|
||||||
|
#
|
||||||
|
# Fhem is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Fhem is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with fhem. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
# Changelog:
|
||||||
|
#
|
||||||
|
# 2016-07-26 initial release
|
||||||
|
# 2016-07-28 added eta, readings in minutes
|
||||||
|
# 2016-08-01 changed JSON decoding/encofing, added stateReading attribute, added outputReadings attribute
|
||||||
|
# 2016-08-02 added attribute includeReturn, round minutes & smart zero'ing, avoid negative values, added update burst
|
||||||
|
# 2016-08-05 fixed 3 perl warnings
|
||||||
|
# 2016-08-09 added auto-update if status returns UNKOWN_ERROR, added outputReading average
|
||||||
|
# 2016-09-25 bugfix Blocking, improved errormessage
|
||||||
|
# 2016-10-07 version 1.0, adding to SVN
|
||||||
|
|
||||||
|
package main;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Time::HiRes qw(gettimeofday);
|
||||||
|
use Data::Dumper;
|
||||||
|
use LWP::Simple qw($ua get);
|
||||||
|
use JSON;
|
||||||
|
use POSIX;
|
||||||
|
use Blocking;
|
||||||
|
|
||||||
|
sub TRAFFIC_Initialize($);
|
||||||
|
sub TRAFFIC_Define($$);
|
||||||
|
sub TRAFFIC_Undef($$);
|
||||||
|
sub TRAFFIC_Set($@);
|
||||||
|
sub TRAFFIC_Attr(@);
|
||||||
|
sub TRAFFIC_GetUpdate($);
|
||||||
|
|
||||||
|
my %TRcmds = (
|
||||||
|
'update' => 'noArg',
|
||||||
|
);
|
||||||
|
my $TRVersion = '1.0';
|
||||||
|
|
||||||
|
sub TRAFFIC_Initialize($){
|
||||||
|
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
$hash->{DefFn} = "TRAFFIC_Define";
|
||||||
|
$hash->{UndefFn} = "TRAFFIC_Undef";
|
||||||
|
$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 includeReturn:0,1 " .
|
||||||
|
$readingFnAttributes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sub TRAFFIC_Define($$){
|
||||||
|
|
||||||
|
my ($hash, $allDefs) = @_;
|
||||||
|
|
||||||
|
my @deflines = split('\n',$allDefs);
|
||||||
|
my @apiDefs = split('[ \t]+', shift @deflines);
|
||||||
|
|
||||||
|
if(int(@apiDefs) < 3) {
|
||||||
|
return "too few parameters: 'define <name> TRAFFIC <APIKEY>'";
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash->{NAME} = $apiDefs[0];
|
||||||
|
$hash->{APIKEY} = $apiDefs[2];
|
||||||
|
$hash->{VERSION} = $TRVersion;
|
||||||
|
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
#clear all readings
|
||||||
|
foreach my $clearReading ( keys %{$hash->{READINGS}}){
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) READING: $clearReading deleted";
|
||||||
|
delete($hash->{READINGS}{$clearReading});
|
||||||
|
}
|
||||||
|
|
||||||
|
# basic update interval
|
||||||
|
if(scalar(@apiDefs) > 3 && $apiDefs[3] =~ m/^\d+$/){
|
||||||
|
$hash->{Interval} = $apiDefs[3];
|
||||||
|
}else{
|
||||||
|
$hash->{Interval} = 3600;
|
||||||
|
}
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) defined ".$hash->{NAME}.' with interval set to '.$hash->{Interval};
|
||||||
|
|
||||||
|
# put in default verbose level
|
||||||
|
$attr{$name}{"verbose"} = 1 if !$attr{$name}{"verbose"};
|
||||||
|
$attr{$name}{"outputReadings"} = "text" if !$attr{$name}{"outputReadings"};
|
||||||
|
|
||||||
|
readingsSingleUpdate( $hash, "state", "Initialized", 1 );
|
||||||
|
|
||||||
|
my $firstTrigger = gettimeofday() + 2;
|
||||||
|
$hash->{TRIGGERTIME} = $firstTrigger;
|
||||||
|
$hash->{TRIGGERTIME_FMT} = FmtDateTime($firstTrigger);
|
||||||
|
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
InternalTimer($firstTrigger, "TRAFFIC_StartUpdate", $hash, 0);
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) InternalTimer set to call GetUpdate in 2 seconds for the first time";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub TRAFFIC_Undef($$){
|
||||||
|
|
||||||
|
my ( $hash, $arg ) = @_;
|
||||||
|
RemoveInternalTimer ($hash);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Attr command
|
||||||
|
#########################################################################
|
||||||
|
sub TRAFFIC_Attr(@){
|
||||||
|
|
||||||
|
my ($cmd,$name,$attrName,$attrValue) = @_;
|
||||||
|
# $cmd can be "del" or "set"
|
||||||
|
# $name is device name
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
|
if ($cmd eq "set") {
|
||||||
|
addToDevAttrList($name, $attrName);
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) attrName $attrName set to attrValue $attrValue";
|
||||||
|
}
|
||||||
|
if($attrName eq "disable" && $attrValue eq "1"){
|
||||||
|
readingsSingleUpdate( $hash, "state", "disabled", 1 );
|
||||||
|
}
|
||||||
|
if($attrName eq "outputReadings" || $attrName eq "includeReturn"){
|
||||||
|
#clear all readings
|
||||||
|
foreach my $clearReading ( keys %{$hash->{READINGS}}){
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) READING: $clearReading deleted";
|
||||||
|
delete($hash->{READINGS}{$clearReading});
|
||||||
|
}
|
||||||
|
# start update
|
||||||
|
InternalTimer(gettimeofday() + 1, "TRAFFIC_StartUpdate", $hash, 0);
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub TRAFFIC_Set($@){
|
||||||
|
|
||||||
|
my ($hash, @param) = @_;
|
||||||
|
return "\"set <TRAFFIC>\" needs at least one argument: \n".join(" ",keys %TRcmds) if (int(@param) < 2);
|
||||||
|
|
||||||
|
my $name = shift @param;
|
||||||
|
my $set = shift @param;
|
||||||
|
|
||||||
|
$hash->{VERSION} = $TRVersion if $hash->{VERSION} != $TRVersion;
|
||||||
|
|
||||||
|
if(AttrVal($name, "disable", 0 ) == 1){
|
||||||
|
readingsSingleUpdate( $hash, "state", "disabled", 1 );
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) is disabled, $set not set!";
|
||||||
|
return undef;
|
||||||
|
}else{
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) set $name $set";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $validCmds = join("|",keys %TRcmds);
|
||||||
|
if($set !~ m/$validCmds/ ) {
|
||||||
|
return join(' ', keys %TRcmds);
|
||||||
|
|
||||||
|
}elsif($set =~ m/update/){
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) update command recieved";
|
||||||
|
|
||||||
|
# if update burst ist specified
|
||||||
|
if( (my $burstCount = shift @param) && (my $burstInterval = shift @param)){
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) update burst is set to $burstCount $burstInterval";
|
||||||
|
$hash->{BURSTCOUNT} = $burstCount;
|
||||||
|
$hash->{BURSTINTERVAL} = $burstInterval;
|
||||||
|
}else{
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) no update burst set";
|
||||||
|
}
|
||||||
|
|
||||||
|
# update internal timer and update NOW
|
||||||
|
my $updateTrigger = gettimeofday() + 1;
|
||||||
|
$hash->{TRIGGERTIME} = $updateTrigger;
|
||||||
|
$hash->{TRIGGERTIME_FMT} = FmtDateTime($updateTrigger);
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
|
||||||
|
# start update
|
||||||
|
InternalTimer($updateTrigger, "TRAFFIC_StartUpdate", $hash, 0);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub TRAFFIC_StartUpdate($){
|
||||||
|
|
||||||
|
my ( $hash ) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
if(AttrVal($name, "disable", 0 ) == 1){
|
||||||
|
RemoveInternalTimer ($hash);
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) is disabled";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
if ( $hash->{Interval}) {
|
||||||
|
RemoveInternalTimer ($hash);
|
||||||
|
my $nextTrigger = gettimeofday() + $hash->{Interval};
|
||||||
|
|
||||||
|
|
||||||
|
if(defined($hash->{BURSTCOUNT}) && $hash->{BURSTCOUNT} > 0){
|
||||||
|
$nextTrigger = gettimeofday() + $hash->{BURSTINTERVAL};
|
||||||
|
$hash->{BURSTCOUNT}--;
|
||||||
|
}elsif(defined($hash->{BURSTCOUNT}) && $hash->{BURSTCOUNT} == 0){
|
||||||
|
delete($hash->{BURSTCOUNT});
|
||||||
|
delete($hash->{BURSTINTERVAL});
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash->{TRIGGERTIME} = $nextTrigger;
|
||||||
|
$hash->{TRIGGERTIME_FMT} = FmtDateTime($nextTrigger);
|
||||||
|
InternalTimer($nextTrigger, "TRAFFIC_StartUpdate", $hash, 0);
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) internal interval timer set to call StartUpdate again in " . int($hash->{Interval}). " seconds";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(defined(AttrVal($name, "start_address", undef )) && defined(AttrVal($name, "end_address", undef ))){
|
||||||
|
|
||||||
|
BlockingCall("TRAFFIC_DoUpdate",$hash->{NAME}.';;;normal',"TRAFFIC_FinishUpdate",60,"TRAFFIC_AbortUpdate",$hash);
|
||||||
|
|
||||||
|
if(defined(AttrVal($name, "includeReturn", undef )) && AttrVal($name, "includeReturn", undef ) eq 1){
|
||||||
|
BlockingCall("TRAFFIC_DoUpdate",$hash->{NAME}.';;;return',"TRAFFIC_FinishUpdate",60,"TRAFFIC_AbortUpdate",$hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
readingsSingleUpdate( $hash, "state", "incomplete configuration", 1 );
|
||||||
|
Log3 $hash, 1, "TRAFFIC: ($name) is not configured correctly, please add start_address and end_address";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub TRAFFIC_AbortUpdate($){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub TRAFFIC_DoUpdate(){
|
||||||
|
|
||||||
|
my ($string) = @_;
|
||||||
|
my ($hName, $direction) = split(";;;", $string); # direction is normal or return
|
||||||
|
my $hash = $defs{$hName};
|
||||||
|
|
||||||
|
my $dotrigger = 1;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
my ($sec,$min,$hour,$dayn,$month,$year,$wday,$yday,$isdst) = localtime(time);
|
||||||
|
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) TRAFFIC_DoUpdate start";
|
||||||
|
|
||||||
|
if ( $hash->{Interval}) {
|
||||||
|
RemoveInternalTimer ($hash);
|
||||||
|
my $nextTrigger = gettimeofday() + $hash->{Interval};
|
||||||
|
$hash->{TRIGGERTIME} = $nextTrigger;
|
||||||
|
$hash->{TRIGGERTIME_FMT} = FmtDateTime($nextTrigger);
|
||||||
|
InternalTimer($nextTrigger, "TRAFFIC_DoUpdate", $hash, 0);
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) internal interval timer set to call GetUpdate again in " . int($hash->{Interval}). " seconds";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $returnJSON;
|
||||||
|
|
||||||
|
my $TRlanguage = '';
|
||||||
|
if(defined(AttrVal($name,"language",undef))){
|
||||||
|
$TRlanguage = '&language='.AttrVal($name,"language","");
|
||||||
|
}else{
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) no language specified";
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $origin = AttrVal($name, "start_address", 0 );
|
||||||
|
my $destination = AttrVal($name, "end_address", 0 );
|
||||||
|
|
||||||
|
if($direction eq "return"){
|
||||||
|
$origin = AttrVal($name, "end_address", 0 );
|
||||||
|
$destination = AttrVal($name, "start_address", 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
my $url = 'https://maps.googleapis.com/maps/api/directions/json?origin='.$origin.'&destination='.$destination.'&mode=driving'.$TRlanguage.'&departure_time=now'.$TRwaypoints.'&key='.$hash->{APIKEY};
|
||||||
|
Log3 $hash, 2, "TRAFFIC: ($name) using $url";
|
||||||
|
|
||||||
|
my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 } );
|
||||||
|
$ua->default_header("HTTP_REFERER" => "www.google.de");
|
||||||
|
my $body = $ua->get($url);
|
||||||
|
my $json = decode_json($body->decoded_content);
|
||||||
|
|
||||||
|
my $duration_sec = $json->{'routes'}[0]->{'legs'}[0]->{'duration'}->{'value'} ;
|
||||||
|
my $duration_in_traffic_sec = $json->{'routes'}[0]->{'legs'}[0]->{'duration_in_traffic'}->{'value'};
|
||||||
|
|
||||||
|
$returnJSON->{'duration'} = $json->{'routes'}[0]->{'legs'}[0]->{'duration'}->{'text'} if AttrVal($name, "outputReadings", "" ) =~ m/text/;
|
||||||
|
$returnJSON->{'duration_in_traffic'} = $json->{'routes'}[0]->{'legs'}[0]->{'duration_in_traffic'}->{'text'} if AttrVal($name, "outputReadings", "" ) =~ m/text/;
|
||||||
|
$returnJSON->{'distance'} = $json->{'routes'}[0]->{'legs'}[0]->{'distance'}->{'text'} if AttrVal($name, "outputReadings", "" ) =~ m/text/;
|
||||||
|
$returnJSON->{'state'} = $json->{'status'};
|
||||||
|
$returnJSON->{'status'} = $json->{'status'};
|
||||||
|
$returnJSON->{'eta'} = FmtTime( gettimeofday() + $duration_in_traffic_sec );
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
$returnJSON->{'delay_min'} = int($duration_in_traffic_sec - $duration_sec) if AttrVal($name, "outputReadings", "" ) =~ m/min/;
|
||||||
|
if(defined($returnJSON->{'delay_min'})){
|
||||||
|
if( ( $returnJSON->{'delay_min'} && $returnJSON->{'delay_min'} =~ m/^-/ ) || $returnJSON->{'delay_min'} < 60){
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) delay_min was negative or less than 1min (".$returnJSON->{'delay_min'}."), set to 0";
|
||||||
|
$returnJSON->{'delay_min'} = 0;
|
||||||
|
}else{
|
||||||
|
$returnJSON->{'delay_min'} = int($returnJSON->{'delay_min'} / 60 + 0.5); #divide 60 and round
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
Log3 $hash, 1, "TRAFFIC: ($name) did not receive duration_in_traffic, not able to calculate delay";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# condition based values
|
||||||
|
$returnJSON->{'error_message'} = $json->{'error_message'} if $json->{'error_message'};
|
||||||
|
# output readings
|
||||||
|
$returnJSON->{'duration_min'} = int($duration_sec / 60 + 0.5) if AttrVal($name, "outputReadings", "" ) =~ m/min/;
|
||||||
|
$returnJSON->{'duration_in_traffic_min'} = int($duration_in_traffic_sec / 60 + 0.5) if AttrVal($name, "outputReadings", "" ) =~ m/min/;
|
||||||
|
$returnJSON->{'duration_sec'} = $duration_sec if AttrVal($name, "outputReadings", "" ) =~ m/sec/;
|
||||||
|
$returnJSON->{'duration_in_traffic_sec'} = $duration_in_traffic_sec if AttrVal($name, "outputReadings", "" ) =~ m/sec/;
|
||||||
|
# raw data (seconds)
|
||||||
|
$returnJSON->{'distance'} = $json->{'routes'}[0]->{'legs'}[0]->{'distance'}->{'value'} if AttrVal($name, "raw_data", 0);
|
||||||
|
|
||||||
|
|
||||||
|
# average readings
|
||||||
|
if(AttrVal($name, "outputReadings", "" ) =~ m/average/){
|
||||||
|
|
||||||
|
# calc average
|
||||||
|
$returnJSON->{'average_duration_min'} = int($hash->{READINGS}{'average_duration_min'}{VAL} + $returnJSON->{'duration_min'}) / 2 if $returnJSON->{'duration_min'};
|
||||||
|
$returnJSON->{'average_duration_in_traffic_min'} = int($hash->{READINGS}{'average_duration_in_traffic_min'}{VAL} + $returnJSON->{'duration_in_traffic_min'}) / 2 if $returnJSON->{'duration_in_traffic_min'};
|
||||||
|
$returnJSON->{'average_delay_min'} = int($hash->{READINGS}{'average_delay_min'}{VAL} + $returnJSON->{'delay_min'}) / 2 if $returnJSON->{'delay_min'};
|
||||||
|
|
||||||
|
# override if this is the first average
|
||||||
|
$returnJSON->{'average_duration_min'} = $returnJSON->{'duration_min'} if !$hash->{READINGS}{'average_duration_min'}{VAL};
|
||||||
|
$returnJSON->{'average_duration_in_traffic_min'} = $returnJSON->{'duration_in_traffic_min'} if !$hash->{READINGS}{'average_duration_in_traffic_min'}{VAL};
|
||||||
|
$returnJSON->{'average_delay_min'} = $returnJSON->{'delay_min'} if !$hash->{READINGS}{'average_delay_min'}{VAL};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) returning from TRAFFIC_DoUpdate: ".encode_json($returnJSON);
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) TRAFFIC_DoUpdate done";
|
||||||
|
return "$name;;;$direction;;;".encode_json($returnJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub TRAFFIC_FinishUpdate($){
|
||||||
|
my ($name,$direction,$rawJson) = split(/;;;/,shift);
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
my %sensors;
|
||||||
|
my $dotrigger = 1;
|
||||||
|
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) TRAFFIC_FinishUpdate start";
|
||||||
|
|
||||||
|
my $json = decode_json($rawJson);
|
||||||
|
readingsBeginUpdate($hash);
|
||||||
|
|
||||||
|
foreach my $readingName (keys %{$json}){
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) ReadingsUpdate: $readingName - ".$json->{$readingName};
|
||||||
|
if($direction eq 'return'){
|
||||||
|
readingsBulkUpdate($hash,'return_'.$readingName,$json->{$readingName});
|
||||||
|
}else{
|
||||||
|
readingsBulkUpdate($hash,$readingName,$json->{$readingName});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($json->{'status'} eq 'UNKNOWN_ERROR'){ # UNKNOWN_ERROR indicates a directions request could not be processed due to a server error. The request may succeed if you try again.
|
||||||
|
InternalTimer(gettimeofday() + 3, "TRAFFIC_StartUpdate", $hash, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(my $stateReading = AttrVal($name,"stateReading",undef)){
|
||||||
|
Log3 $hash, 5, "TRAFFIC: ($name) stateReading defined, override state";
|
||||||
|
if(!$json->{$stateReading}){
|
||||||
|
Log3 $hash, 1, "TRAFFIC: ($name) stateReading $stateReading not found";
|
||||||
|
}else{
|
||||||
|
readingsBulkUpdate($hash,'state',$json->{$stateReading});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readingsEndUpdate($hash, $dotrigger);
|
||||||
|
Log3 $hash, 3, "TRAFFIC: ($name) TRAFFIC_FinishUpdate done";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub prettySeconds {
|
||||||
|
my $time = shift;
|
||||||
|
|
||||||
|
if($time =~ m/^-/){
|
||||||
|
return "0 min";
|
||||||
|
}
|
||||||
|
my $days = int($time / 86400);
|
||||||
|
$time -= ($days * 86400);
|
||||||
|
my $hours = int($time / 3600);
|
||||||
|
$time -= ($hours * 3600);
|
||||||
|
my $minutes = int($time / 60);
|
||||||
|
my $seconds = $time % 60;
|
||||||
|
|
||||||
|
$days = $days < 1 ? '' : $days .' days ';
|
||||||
|
$hours = $hours < 1 ? '' : $hours .' hours ';
|
||||||
|
$minutes = $minutes < 1 ? '' : $minutes . ' min ';
|
||||||
|
$time = $days . $hours . $minutes;
|
||||||
|
if(!$time){
|
||||||
|
return "0 min";
|
||||||
|
}else{
|
||||||
|
return $time;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
#======================================================================
|
||||||
|
#======================================================================
|
||||||
|
#
|
||||||
|
# HTML Documentation for help and commandref
|
||||||
|
#
|
||||||
|
#======================================================================
|
||||||
|
#======================================================================
|
||||||
|
=pod
|
||||||
|
=item device
|
||||||
|
=item summary provide traffic details with Google Distance API
|
||||||
|
=item summary_DE stellt Verkehrsdaten mittels Google Distance API bereit
|
||||||
|
=begin html
|
||||||
|
|
||||||
|
<a name="TRAFFIC"></a>
|
||||||
|
<h3>TRAFFIC</h3>
|
||||||
|
<ul>
|
||||||
|
<u><b>TRAFFIC - google maps directions module</b></u>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
This FHEM module collects and displays data obtained via the google maps directions api<br>
|
||||||
|
requirements:<br>
|
||||||
|
perl JSON module<br>
|
||||||
|
perl LWP::SIMPLE module<br>
|
||||||
|
Google maps API key<br>
|
||||||
|
<br>
|
||||||
|
<b>Features:</b>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
<li>get distance between start and end location</li>
|
||||||
|
<li>get travel time for route</li>
|
||||||
|
<li>get travel time in traffic for route</li>
|
||||||
|
<li>define additional waypoints</li>
|
||||||
|
<li>calculate delay between travel-time and travel-time-in-traffic</li>
|
||||||
|
<li>choose default language</li>
|
||||||
|
<li>disable the device</li>
|
||||||
|
<li>5 log levels</li>
|
||||||
|
<li>get outputs in seconds / meter (raw_data)</li>
|
||||||
|
<li>state of google maps returned in error reading (i.e. The provided API key is invalid)</li>
|
||||||
|
<li>customize update interval (default 3600 seconds)</li>
|
||||||
|
<li>calculate ETA with localtime and delay</li>
|
||||||
|
<li>configure the output readings with attribute outputReadings, text, min sec</li>
|
||||||
|
<li>configure the state-reading </li>
|
||||||
|
<li>optionally display the same route in return</li>
|
||||||
|
<li>one-time-burst, specify the amount and interval between updates</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<a name="TRAFFICdefine"></a>
|
||||||
|
<b>Define:</b>
|
||||||
|
<ul><br>
|
||||||
|
<code>define <name> TRAFFIC <YOUR-API-KEY> [UPDATE-INTERVAL]</code>
|
||||||
|
<br><br>
|
||||||
|
example:<br>
|
||||||
|
<code>define muc2berlin TRAFFIC ABCDEFGHIJKLMNOPQRSTVWYZ 600</code><br>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<b>Attributes:</b>
|
||||||
|
<ul>
|
||||||
|
<li>"start_address" - Street, zipcode City <b>(mandatory)</b></li>
|
||||||
|
<li>"end_address" - Street, zipcode City <b>(mandatory)</b></li>
|
||||||
|
<li>"raw_data" - 0:1</li>
|
||||||
|
<li>"language" - de, en etc.</li>
|
||||||
|
<li>"waypoints" - Lat, Long coordinates, separated by | </li>
|
||||||
|
<li>"disable" - 0:1</li>
|
||||||
|
<li>"stateReading" - name the reading which will be used in device state</li>
|
||||||
|
<li>"outputReadings" - define what kind of readings you want to get: text, min, sec, average</li>
|
||||||
|
<li>"includeReturn" - 0:1</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<a name="TRAFFICreadings"></a>
|
||||||
|
<b>Readings:</b>
|
||||||
|
<ul>
|
||||||
|
<li>delay </li>
|
||||||
|
<li>distance </li>
|
||||||
|
<li>duration </li>
|
||||||
|
<li>duration_in_traffic </li>
|
||||||
|
<li>state </li>
|
||||||
|
<li>eta</li>
|
||||||
|
<li>delay_min</li>
|
||||||
|
<li>duration_min</li>
|
||||||
|
<li>duration_in_traffic_min</li>
|
||||||
|
<li>error_message</li>
|
||||||
|
</ul>
|
||||||
|
<br><br>
|
||||||
|
<a name="TRAFFICset"></a>
|
||||||
|
<b>Set</b>
|
||||||
|
<ul>
|
||||||
|
<li>update [burst-update-count] [burst-update-interval] - update readings manually</li>
|
||||||
|
</ul>
|
||||||
|
<br><br>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
=end html
|
||||||
|
=cut
|
||||||
|
|
@ -694,3 +694,6 @@
|
|||||||
|
|
||||||
- Sat Oct 1 2016 (Christian.Kühnel)
|
- Sat Oct 1 2016 (Christian.Kühnel)
|
||||||
- added new module 99_Venetian
|
- added new module 99_Venetian
|
||||||
|
|
||||||
|
- Fri Oct 7 2016 (jmike)
|
||||||
|
- adding new module 98_TRAFFIC
|
||||||
|
@ -347,6 +347,7 @@ FHEM/98_PID20.pm John http://forum.fhem.de Automatis
|
|||||||
FHEM/98_RandomTimer.pm dietmar63 http://forum.fhem.de Unterstuetzende Dienste/Kalendermodule
|
FHEM/98_RandomTimer.pm dietmar63 http://forum.fhem.de Unterstuetzende Dienste/Kalendermodule
|
||||||
FHEM/98_SVG.pm rudolfkoenig http://forum.fhem.de Frontends/SVG/Plots/logProxy
|
FHEM/98_SVG.pm rudolfkoenig http://forum.fhem.de Frontends/SVG/Plots/logProxy
|
||||||
FHEM/98_THRESHOLD.pm damian-s http://forum.fhem.de Automatisierung
|
FHEM/98_THRESHOLD.pm damian-s http://forum.fhem.de Automatisierung
|
||||||
|
FHEM/98_TRAFFIC.pm jmike http://forum.fhem.de Unterstuetzende Dienste
|
||||||
FHEM/98_UbiquitiPM.pm Wzut http://forum.fhem.de Sonstige Systeme
|
FHEM/98_UbiquitiPM.pm Wzut http://forum.fhem.de Sonstige Systeme
|
||||||
FHEM/98_UbiquitiOut.pm Wzut http://forum.fhem.de Sonstige Systeme
|
FHEM/98_UbiquitiOut.pm Wzut http://forum.fhem.de Sonstige Systeme
|
||||||
FHEM/98_WeekdayTimer.pm dietmar63 http://forum.fhem.de Unterstuetzende Dienste
|
FHEM/98_WeekdayTimer.pm dietmar63 http://forum.fhem.de Unterstuetzende Dienste
|
||||||
|
Loading…
Reference in New Issue
Block a user