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:
parent
9b79866c06
commit
ca03a5ce63
@ -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
|
||||||
|
@ -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,9 +74,11 @@ 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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub TRAFFIC_Define($$){
|
sub TRAFFIC_Define($$){
|
||||||
@ -85,7 +97,6 @@ sub TRAFFIC_Define($$){
|
|||||||
$hash->{VERSION} = $TRVersion;
|
$hash->{VERSION} = $TRVersion;
|
||||||
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};
|
||||||
|
|
||||||
@ -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&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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user