2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-10 09:16:53 +00:00

98_TRAFFIC: new feature, map, return waypoint, v1.2

git-svn-id: https://svn.fhem.de/fhem/trunk@12838 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
jmike 2016-12-19 20:59:43 +00:00
parent 9b79866c06
commit ca03a5ce63
2 changed files with 131 additions and 15 deletions

View File

@ -1,5 +1,6 @@
# 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.
- feature: 98_TRAFFIC: reverse waypoints, integrated map for visualization
- update: 77_UWZ: New Version 1.4.7 added headlines for weblinks - update: 77_UWZ: New Version 1.4.7 added headlines for weblinks
- update: 73_NUKIBridge,74_NUKIDevice: New Version 0.4.0 - update: 73_NUKIBridge,74_NUKIDevice: New Version 0.4.0
More Feature get,set ,Callback/Webhook Funktion More Feature get,set ,Callback/Webhook Funktion

View File

@ -17,6 +17,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with fhem. If not, see <http://www.gnu.org/licenses/>. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
# #
# 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: # Changelog:
# #
@ -30,18 +37,21 @@
# 2016-10-07 version 1.0, adding to SVN # 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-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-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; package main;
use strict; use strict;
use warnings; use warnings;
use Time::HiRes qw(gettimeofday);
use Data::Dumper; use Data::Dumper;
use Time::HiRes qw(gettimeofday);
use LWP::Simple qw($ua get); use LWP::Simple qw($ua get);
use JSON;
use POSIX;
use Blocking; 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_Initialize($);
sub TRAFFIC_Define($$); sub TRAFFIC_Define($$);
@ -53,7 +63,7 @@ sub TRAFFIC_GetUpdate($);
my %TRcmds = ( my %TRcmds = (
'update' => 'noArg', 'update' => 'noArg',
); );
my $TRVersion = '1.1'; my $TRVersion = '1.2';
sub TRAFFIC_Initialize($){ sub TRAFFIC_Initialize($){
@ -64,8 +74,10 @@ sub TRAFFIC_Initialize($){
$hash->{SetFn} = "TRAFFIC_Set"; $hash->{SetFn} = "TRAFFIC_Set";
$hash->{AttrFn} = "TRAFFIC_Attr"; $hash->{AttrFn} = "TRAFFIC_Attr";
$hash->{AttrList} = $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; $readingFnAttributes;
$data{FWEXT}{"/TRAFFIC"}{FUNC} = "TRAFFIC_debug";
$data{FWEXT}{"/TRAFFIC"}{FORKABLE} = 1;
} }
@ -86,7 +98,6 @@ sub TRAFFIC_Define($$){
delete($hash->{BURSTCOUNT}) if $hash->{BURSTCOUNT}; delete($hash->{BURSTCOUNT}) if $hash->{BURSTCOUNT};
delete($hash->{BURSTINTERVAL}) if $hash->{BURSTINTERVAL}; delete($hash->{BURSTINTERVAL}) if $hash->{BURSTINTERVAL};
my $name = $hash->{NAME}; my $name = $hash->{NAME};
#clear all readings #clear all readings
@ -145,7 +156,19 @@ sub TRAFFIC_Attr(@){
if($attrName eq "disable" && $attrValue eq "1"){ if($attrName eq "disable" && $attrValue eq "1"){
readingsSingleUpdate( $hash, "state", "disabled", 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 #clear all readings
foreach my $clearReading ( keys %{$hash->{READINGS}}){ foreach my $clearReading ( keys %{$hash->{READINGS}}){
Log3 $hash, 5, "TRAFFIC: ($name) READING: $clearReading deleted"; Log3 $hash, 5, "TRAFFIC: ($name) READING: $clearReading deleted";
@ -201,7 +224,10 @@ sub TRAFFIC_Set($@){
InternalTimer($updateTrigger, "TRAFFIC_StartUpdate", $hash, 0); InternalTimer($updateTrigger, "TRAFFIC_StartUpdate", $hash, 0);
return undef; return undef;
}elsif($set =~ m/debug/){
# TRAFFIC_widget();
} }
} }
@ -325,13 +351,19 @@ sub TRAFFIC_DoUpdate(){
my $TRwaypoints = ''; my $TRwaypoints = '';
if(defined(AttrVal($name,"waypoints",undef))){ if(defined(AttrVal($name,"waypoints",undef))){
$TRwaypoints = '&waypoints=via:' . join('|via:', split('\|', 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{ }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 ); my $origin = AttrVal($name, "start_address", 0 );
@ -361,6 +393,9 @@ sub TRAFFIC_DoUpdate(){
$returnJSON->{'status'} = $json->{'status'}; $returnJSON->{'status'} = $json->{'status'};
$returnJSON->{'eta'} = FmtTime( gettimeofday() + $duration_in_traffic_sec ) if defined($duration_in_traffic_sec); $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){ if($duration_in_traffic_sec && $duration_sec){
$returnJSON->{'delay'} = prettySeconds($duration_in_traffic_sec - $duration_sec) if AttrVal($name, "outputReadings", "" ) =~ m/text/; $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"; 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"; Log3 $hash, 1, "TRAFFIC: ($name) stateReading $stateReading not found";
} }
} }
readingsEndUpdate($hash, $dotrigger); readingsEndUpdate($hash, $dotrigger);
Log3 $hash, 3, "TRAFFIC: ($name) TRAFFIC_FinishUpdate done"; Log3 $hash, 3, "TRAFFIC: ($name) TRAFFIC_FinishUpdate done";
} }
sub TRAFFIC_weblink{
my $name = shift();
my $return = shift();
return "<a href='/fhem/TRAFFIC_debug?name=$name&return=$return'>open Map for TRAFFIC $name </a><br>";
}
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 = '<script type="text/javascript" src="http://maps.google.com/maps/api/js?libraries=geometry&amp;sensor=false"></script>
<input size="200" type="hidden" id="path" value="'.decode_base64(ReadingsVal($name, "debugPoly", undef) ).'">';
$web .= '<input size="200" type="hidden" id="pathR" value="'.decode_base64(ReadingsVal($name, "return_debugPoly", undef) ).'">' if decode_base64(ReadingsVal($name, "return_debugPoly", undef) );
$web .= '
<div id="map"></div>
<style>
#map {width:800px;height:800px;}
</style>
<script type="text/javascript">
function initialize() {
var myLatlng = new google.maps.LatLng('.ReadingsVal($name, "$debugLocation", undef ).');
var myOptions = {
zoom: 10,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("map"), myOptions);
var decodedPath = google.maps.geometry.encoding.decodePath(document.getElementById("path").value);
var decodedLevels = decodeLevels("");
var setRegion = new google.maps.Polyline({
path: decodedPath,
levels: decodedLevels,
strokeColor: "#4cde44",
strokeOpacity: 1.0,
strokeWeight: 6,
map: map
});';
$web .= 'var decodedPathR = google.maps.geometry.encoding.decodePath(document.getElementById("pathR").value);
var decodedLevelsR = decodeLevels("");
var setRegionR = new google.maps.Polyline({
path: decodedPathR,
levels: decodedLevels,
strokeColor: "#FF0000",
strokeOpacity: 1.0,
strokeWeight: 2,
map: map
});' if decode_base64(ReadingsVal($name, "return_debugPoly", undef) );
$web .='
}
function decodeLevels(encodedLevelsString) {
var decodedLevels = [];
for (var i = 0; i < encodedLevelsString.length; ++i) {
var level = encodedLevelsString.charCodeAt(i) - 63;
decodedLevels.push(level);
}
return decodedLevels;
}
initialize();
</script>';
FW_pO $web;
return ($FW_RETTYPE, $FW_RET);
}
sub prettySeconds { sub prettySeconds {
my $time = shift; my $time = shift;
@ -502,6 +612,7 @@ sub prettySeconds {
requirements:<br> requirements:<br>
perl JSON module<br> perl JSON module<br>
perl LWP::SIMPLE module<br> perl LWP::SIMPLE module<br>
perl MIME::Base64 module<br>
Google maps API key<br> Google maps API key<br>
<br> <br>
<b>Features:</b> <b>Features:</b>
@ -523,6 +634,9 @@ sub prettySeconds {
<li>configure the state-reading </li> <li>configure the state-reading </li>
<li>optionally display the same route in return</li> <li>optionally display the same route in return</li>
<li>one-time-burst, specify the amount and interval between updates</li> <li>one-time-burst, specify the amount and interval between updates</li>
<li>different Travel Modes (driving, walking, bicycling and transit)</li>
<li>flexible update schedule</li>
<li>integrated Map to visualize configured route at verbose 5</li>
</ul> </ul>
<br> <br>
<br> <br>
@ -543,6 +657,7 @@ sub prettySeconds {
<li>"raw_data" - 0:1</li> <li>"raw_data" - 0:1</li>
<li>"language" - de, en etc.</li> <li>"language" - de, en etc.</li>
<li>"waypoints" - Lat, Long coordinates, separated by | </li> <li>"waypoints" - Lat, Long coordinates, separated by | </li>
<li>"returnWaypoints" - Lat, Long coordinates, separated by | </li>
<li>"disable" - 0:1</li> <li>"disable" - 0:1</li>
<li>"stateReading" - name the reading which will be used in device state</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>"outputReadings" - define what kind of readings you want to get: text, min, sec, average</li>