History:
01.05.24: - S-Investor Code noch einmal angepasst, damit er mit allen Wertpapieren funktioniert
- Yahoo API sollte wieder funktionieren
- das Attribut stocks wurde noch einmal geändert und der zusätzliche Doppelpunkt wieder entfernt. Stattdessen wird jetzt | als Trennzeichen verwendet.
13.04.24: - S-Investor Webseite wurde geändert und der Code wurde angepasst
- Yahoo API wurde geändert es konnte aber bisher keine Lösung gefunden werden (bis auf weiteres muss deshalb S-Investor verwendet werden
- das Attribut stocks wurde geändert und enthält ein alternatives Symbol, welches auch leer gelassen werden kann. Der zusätzliche Doppelunkt darf aber nicht entfallen!
17.03.24: - Yahoo API Änderung nachgezogen
05.02.24: - Yahoo API Änderung nachgezogen
23.11.23: - Xetra und Tradegate Variante wieder lauffähig gemacht, nachdem die Webseite geändert wurde
24.07.23: - diverse Bugs und Probleme wurden behoben
- es kann jetzt zwischen 4 APIs gewählt werden (2 Yahoo Abfragemöglichkeiten, XETRA und Tradegate Abfrage über S-Investor)
20.07.23: - Probleme beim Einlesen des Stocks Attributes behoben
- Abfragezeiten wurden zeitlich auf übliche Börsenzeiten beschränkt
14.07.23: - prozentuale Änderung war zu hoch und wurde behoben
- ein DOIF Template für Charts wurde hinzugefügt
13.07.23: - update Problem hoffentlich behoben
- Umstellung des Codes auf eine andere Yahoo API die momentan nicht geblockt wird
- jeder Yahoo Request wird um 10s verzögert, um ein Blocking zu vermeiden
- der Update Zyklus wurde auf 30min erhöht
Hier habe ich mal eine erste Version zusammen gebaut, um über DOIF eine tabellarische Übersicht von Aktienkursen darzustellen. Ich denke den Code kann jeder für sich relativ leicht anpassen und erweitern. Falls ihr noch Ideen dazu habt und ich entsprechend Zeit finde, kann ich mir das auch eventuell ansehen.
defmod doif_Stocks DOIF subs\
{\
use utf8;;\
use Blocking;;\
use Scalar::Util qw(looks_like_number);;\
use LWP::UserAgent;;\
use HTTP::Request::Common;;\
use HTTP::Cookies;;\
use URI::Escape;;\
use JSON qw(decode_json);;\
use HTML::Entities;;\
use HTML::TreeBuilder;;\
use Web::Scraper;;\
use Data::Dumper qw(Dumper);;\
\
$_stocks = ::AttrVal("$SELF", 'stocks', '');;\
$_api = ::AttrVal("$SELF", 'api', 'YahooJSON1');;\
\
sub startUpdate($)\
{\
my $name = shift;;\
$_stocks = ::AttrVal($name, 'stocks', '');;\
$_api = ::AttrVal($name, 'api', 'YahooJSON1');;\
my @stocks = split(',', $_stocks);;\
## max 3 retries (always wait 10s between each request)\
my $timeout = (scalar(@stocks) * 65) + 60;;\
\
::Log3 $name, 5, $name.': stocks = '.$_stocks;;\
::Log3 $name, 5, $name.': api = '.$_api;;\
\
if (defined($_blockingcalls{'PID_UPDATE'.$name}))\
{\
::Log3 $name, 3, $name.': Blocking call already running (update).';;\
\
::BlockingKill($_blockingcalls{'PID_UPDATE'.$name});;\
}\
\
::Log3 $name, 3, $name.': Blocking call started (update).';;\
\
$_blockingcalls{'PID_UPDATE'.$name} = ::BlockingCall('DOIF::doUpdate', $name, 'DOIF::endUpdate', $timeout, 'DOIF::abortUpdate', $name);;\
}\
\
sub DOIF::doUpdate($)\
{\
my $name = shift;;\
my $output = '';;\
my $url_template = '';; \
my @stocks = split(',', $_stocks);;\
\
if ($_api eq 'YahooJSON1')\
{\
$url_template = 'https://query2.finance.yahoo.com/v7/finance/options/?symbols=#REPLACE#';; \
} \
elsif ($_api eq 'YahooJSON2')\
{\
$url_template = 'https://query2.finance.yahoo.com/v11/finance/quoteSummary/?symbols=#REPLACE#&modules=price';;\
}\
elsif ($_api eq 'S-InvestorXETRA')\
{\
$url_template = 'https://web.s-investor.de/app/detail.htm?INST_ID=0003329&boerse=GER&isin=#REPLACE#';;\
}\
elsif ($_api eq 'S-InvestorTRADEGATE')\
{\
$url_template = 'https://web.s-investor.de/app/detail.htm?INST_ID=0003329&boerse=TDG&isin=#REPLACE#';;\
}\
\
foreach my $stock (@stocks)\
{\
my ($stock_symbol, $stock_count, $stock_initvalue) = split(':', $stock);;\
my ($stock_symbol_main, $stock_symbol_alt) = split('\|', $stock_symbol);;\
my $url = $url_template;;\
\
$stock_symbol_alt = '' if (!defined($stock_symbol_alt));;\
\
## use alternative symbol if needed\
if ((($_api =~ /YahooJSON/) && length($stock_symbol_main) == 12 && looks_like_number(substr($stock_symbol_main, -1))) ||\
(($_api =~ /S-Investor/) && !(length($stock_symbol_main) == 12 && looks_like_number(substr($stock_symbol_main, -1)))))\
{\
$url =~ s/#REPLACE#/$stock_symbol_alt/;;\
}\
else\
{\
$url =~ s/#REPLACE#/$stock_symbol_main/;;\
}\
\
my $ua = LWP::UserAgent->new;;\
my $reply;;\
\
$ua->agent('Mozilla/5.0 (Macintosh;; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36');;\
\
## retry up to 3 times\
for (my $i = 0;; $i < 3;; $i++)\
{\
if (($_api eq 'YahooJSON1') || ($_api eq 'YahooJSON2'))\
{\
my %opts = @LWP::Protocol::http::EXTRA_SOCK_OPTS;;\
$opts{MaxLineLength} = 16384;;\
@LWP::Protocol::http::EXTRA_SOCK_OPTS = %opts;;\
\
my $gcrumb = "";;\
$ua->add_handler("response_redirect", sub \
{\
my($response, $ua, $h) = @_;;\
my $redirect_uri = URI->new($response->header("Location"));;\
\
if ($redirect_uri->path eq "/consent") \
{\
my %params = $redirect_uri->query_form;;\
$gcrumb = $params{'gcrumb'};;\
} \
elsif ($redirect_uri->path eq "/v2/collectConsent") \
{\
my %params = $redirect_uri->query_form;;\
my $sessionId = $params{'sessionId'};;\
my $request = POST($redirect_uri, [\
'csrfToken' => $gcrumb,\
'sessionId' => $sessionId,\
'originalDoneUrl' => 'https://www.yahoo.com/?guccounter=1',\
'namespace' => 'yahoo',\
'reject' => 'reject'\
]);;\
return $request;;\
}\
return;;\
});;\
\
my $cookie_jar = HTTP::Cookies->new;;\
$ua->cookie_jar($cookie_jar);;\
\
$ua->requests_redirectable(['GET', 'HEAD', 'POST']);;\
\
$reply = $ua->get('https://www.yahoo.com/', "Accept" => "text/html");;\
$reply = $ua->request(GET 'https://query2.finance.yahoo.com/v1/test/getcrumb');;\
my $crumb = uri_escape($reply->content);;\
\
$url .= '&crumb='.$crumb;;\
$reply = $ua->request(GET $url);;\
\
if (200 == $reply->code)\
{\
my $json_data = JSON::decode_json $reply->content;;\
my $json_data_count;;\
$json_data_count = scalar @{ $json_data->{'optionChain'}{'result'} } if ($_api eq 'YahooJSON1');;\
$json_data_count = scalar @{ $json_data->{'quoteSummary'}{'result'} } if ($_api eq 'YahooJSON2');;\
\
if ($json_data_count >= 1)\
{\
my $json_resources_price;;\
my $json_symbol = '';;\
my $json_shortname = '';;\
my $json_longname = '';;\
my $json_type = '';;\
my $json_currency = '';;\
my $json_exchangeSymbol = '';;\
my $json_exchangeName = '';;\
my $json_delay = '';;\
my $json_marketState = '';;\
my $json_timestamp = '';;\
my $json_volume = '';;\
my $json_open = '';;\
my $json_high = '';;\
my $json_low = '';;\
my $json_close = '';;\
my $json_change = '';;\
my $json_changep = '';;\
my $json_price = '';; \
\
$json_resources_price = $json_data->{'optionChain'}{'result'}[0]{'quote'} if ($_api eq 'YahooJSON1');;\
$json_resources_price = $json_data->{'quoteSummary'}{'result'}[0]{'price'} if ($_api eq 'YahooJSON2');;\
$json_symbol = (exists($json_resources_price->{'symbol'}) ? $json_resources_price->{'symbol'} : '');;\
$json_shortname = (exists($json_resources_price->{'shortName'}) ? $json_resources_price->{'shortName'} : '');;\
$json_longname = (exists($json_resources_price->{'longName'}) ? $json_resources_price->{'longName'} : '');;\
$json_type = (exists($json_resources_price->{'quoteType'}) ? $json_resources_price->{'quoteType'} : '');;\
$json_currency = (exists($json_resources_price->{'currency'}) ? $json_resources_price->{'currency'} : '');;\
$json_exchangeSymbol = (exists($json_resources_price->{'exchange'}) ? $json_resources_price->{'exchange'} : '');;\
$json_exchangeName = (exists($json_resources_price->{'exchangeName'}) ? $json_resources_price->{'exchangeName'} : '');;\
$json_delay = (exists($json_resources_price->{'exchangeDataDelayedBy'}) ? $json_resources_price->{'exchangeDataDelayedBy'} : '');;\
$json_marketState = (exists($json_resources_price->{'marketState'}) ? $json_resources_price->{'marketState'} : '');;\
\
if ($_api eq 'YahooJSON1')\
{\
$json_timestamp = (exists($json_resources_price->{'regularMarketTime'}) ? $json_resources_price->{'regularMarketTime'} : '');;\
$json_volume = (exists($json_resources_price->{'regularMarketVolume'}) ? $json_resources_price->{'regularMarketVolume'} : '');;\
$json_open = (exists($json_resources_price->{'regularMarketOpen'}) ? $json_resources_price->{'regularMarketOpen'} : '');;\
$json_high = (exists($json_resources_price->{'regularMarketDayHigh'}) ? $json_resources_price->{'regularMarketDayHigh'} : '');;\
$json_low = (exists($json_resources_price->{'regularMarketDayLow'}) ? $json_resources_price->{'regularMarketDayLow'} : '');;\
$json_close = (exists($json_resources_price->{'regularMarketPreviousClose'}) ? $json_resources_price->{'regularMarketPreviousClose'} : '');;\
$json_change = (exists($json_resources_price->{'regularMarketChange'}) ? $json_resources_price->{'regularMarketChange'} : '');;\
$json_changep = (exists($json_resources_price->{'regularMarketChangePercent'}) ? $json_resources_price->{'regularMarketChangePercent'} : '');;\
$json_price = (exists($json_resources_price->{'regularMarketPrice'}) ? $json_resources_price->{'regularMarketPrice'} : '');;\
}\
elsif ($_api eq 'YahooJSON2')\
{\
$json_timestamp = (exists($json_resources_price->{'regularMarketChange'}) && exists($json_resources_price->{'regularMarketChange'}{'regularMarketTime'}) ?\
$json_resources_price->{'regularMarketChange'}{'regularMarketTime'} : '');;\
$json_volume = (exists($json_resources_price->{'regularMarketVolume'}) && exists($json_resources_price->{'regularMarketVolume'}{'raw'}) ?\
$json_resources_price->{'regularMarketVolume'}{'raw'} : '');;\
$json_open = (exists($json_resources_price->{'regularMarketOpen'}) && exists($json_resources_price->{'regularMarketOpen'}{'raw'}) ?\
$json_resources_price->{'regularMarketOpen'}{'raw'} : '');;\
$json_high = (exists($json_resources_price->{'regularMarketDayHigh'}) && exists($json_resources_price->{'regularMarketDayHigh'}{'raw'}) ?\
$json_resources_price->{'regularMarketDayHigh'}{'raw'} : '');;\
$json_low = (exists($json_resources_price->{'regularMarketDayLow'}) && exists($json_resources_price->{'regularMarketDayLow'}{'raw'}) ?\
$json_resources_price->{'regularMarketDayLow'}{'raw'} : '');;\
$json_close = (exists($json_resources_price->{'regularMarketPreviousClose'}) && exists($json_resources_price->{'regularMarketPreviousClose'}{'raw'}) ?\
$json_resources_price->{'regularMarketPreviousClose'}{'raw'} : '');;\
$json_change = (exists($json_resources_price->{'regularMarketChange'}) && exists($json_resources_price->{'regularMarketChange'}{'raw'}) ?\
$json_resources_price->{'regularMarketChange'}{'raw'} : '');;\
$json_changep = (exists($json_resources_price->{'regularMarketChangePercent'}) && exists($json_resources_price->{'regularMarketChangePercent'}{'raw'}) ?\
$json_resources_price->{'regularMarketChangePercent'}{'raw'} : '');;\
$json_price = (exists($json_resources_price->{'regularMarketPrice'}) && exists($json_resources_price->{'regularMarketPrice'}{'raw'}) ?\
$json_resources_price->{'regularMarketPrice'}{'raw'} : '');;\
}\
\
## replace | by _\
$json_longname = '' if (!defined($json_longname));;\
$json_longname = decode_entities($json_longname) if ($json_longname ne '');;\
$json_longname =~ s/\|/_/g;;\
$json_shortname =~ s/\|/_/g;;\
\
## convert to %\
$json_changep = $json_changep * 100 if (($json_changep ne '') && ($_api eq 'YahooJSON2'));;\
\
$stock_count = 1 if (!defined($stock_count));;\
$stock_initvalue = 0 if (!defined($stock_initvalue));;\
\
if (($json_currency eq "GBp") ||\
($json_currency eq "GBX") ||\
($json_currency eq "ZAc"))\
{\
$json_price = $json_price / 100 if ($json_price ne '');;\
$json_open = $json_open / 100 if ($json_open ne '');;\
$json_high = $json_high / 100 if ($json_high ne '');;\
$json_low = $json_low / 100 if ($json_low ne '');;\
$json_close = $json_close / 100 if ($json_close ne '');;\
$json_change = $json_change / 100 if ($json_change ne '');;\
}\
\
if (($json_currency eq "GBp") ||\
($json_currency eq "GBX"))\
{\
$json_currency = "GBP";;\
}\
elsif ($json_currency eq "ZAc")\
{\
$json_currency = "ZAR";;\
}\
\
$output .= '|'.$stock_symbol_main.'|'.$json_symbol.'|'.$json_shortname.'|'.$json_longname.'|'.$json_type.'|'.$json_currency.'|'.\
$json_exchangeSymbol.'|'.$json_exchangeName.'|'.$json_marketState.'|'.$json_timestamp.'|'.\
$json_volume.'|'.$json_open.'|'.$json_high.'|'.$json_low.'|'.$json_close.'|'.$json_change.'|'.\
$json_changep.'|'.$json_price.'|'.$json_delay.'|'.$stock_count.'|'.$stock_initvalue;;\
\
sleep(10);;\
last;;\
}\
}\
}\
elsif (($_api eq 'S-InvestorXETRA') || ($_api eq 'S-InvestorTRADEGATE'))\
{\
my $type = '';; \
my $exchangeSymbol;;\
my $delay = '';;\
my $marketState = '';;\
my $open = '';;\
my $high = '';;\
my $low = '';;\
my $shortname = '';;\
\
$exchangeSymbol = 'GER' if ($_api eq 'S-InvestorXETRA');;\
$exchangeSymbol = 'TDG' if ($_api eq 'S-InvestorTRADEGATE');;\
\
eval\
{\
my $tree = HTML::TreeBuilder->new_from_url($url);;\
\
my $lastvalue = $tree->look_down('id'=>'kursdaten');;\
\
my $td1 = ($lastvalue->look_down('_tag'=>'td'))[1];;\
my @child = $td1->content_list;;\
my $symbol = $child[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[3];;\
@child = $td1->content_list;;\
my $longname = $child[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[5];;\
@child = $td1->content_list;;\
my $exchangeName = $child[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[7];;\
@child = $td1->content_list;;\
my $timestamp = substr($child[0], 0, 8).' '.substr($child[0], 9, 5);;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[9];;\
@child = $td1->content_list;;\
my $price = $child[0];;\
$price =~ s/\.//g;;\
$price =~ s/,/\./;;\
my $encprice = encode_entities($price);;\
my @splitprice = split ('&',$encprice);;\
$price = $splitprice[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[11];;\
@child = $td1->content_list;;\
my $currency = $child[0];;\
$currency =~ s/Euro/EUR/;;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[13];;\
@child = $td1->content_list;;\
my $volume = $child[0];;\
\
my @searchvalue = $tree->look_down('class'=>'contentBox oneColum');;\
foreach (@searchvalue)\
{\
if (($_->content_list)[0]{'_content'}[0]{'_content'}[0] eq 'Aktuelle Vergleichszahlen')\
{\
#-- change (absolute change)\
$td1 = ($_->look_down('_tag'=>'td'))[13];;\
@child = $td1->content_list;;\
my $change = $child[0];;\
$change =~ s/\.//g;;\
$change =~ s/,/\./;;\
my $encchange = encode_entities($change);;\
my @splitchange = split ('&',$encchange);;\
$change = $splitchange[0];;\
$change = ($splitchange[0] eq '-') ? '' : $splitchange[0];;\
\
#-- p_change (relative change)\
$td1 = ($_->look_down('_tag'=>'td'))[16];;\
@child = $td1->content_list;;\
my $changep = $child[0];;\
$changep =~ s/[\.|%]//g;;\
$changep =~ s/,/\./;;\
$changep = '' if ($changep eq '-');;\
\
#-- close\
$td1 = ($_->look_down('_tag'=>'td'))[34];;\
@child = $td1->content_list;;\
my $close = $child[0];;\
$close =~ s/\.//g;;\
$close =~ s/,/\./;;\
my $encclose = encode_entities($close);;\
my @splitclose = split ('&', $encclose);;\
$close = ($splitclose[0] eq '-') ? '' : $splitclose[0];;\
\
## replace | by _\
$longname =~ s/(\||&;)/_/g;;\
if ($shortname eq '') \
{\
$shortname = $longname;;\
}\
else\
{\
$shortname =~ s/\|/_/g;;\
}\
\
$stock_count = 1 if (!defined($stock_count));;\
$stock_initvalue = 0 if (!defined($stock_initvalue));;\
\
$output .= '|'.$stock_symbol_main.'|'.$symbol.'|'.$shortname.'|'.$longname.'|'.$type.'|'.$currency.'|'.\
$exchangeSymbol.'|'.$exchangeName.'|'.$marketState.'|'.$timestamp.'|'.\
$volume.'|'.$open.'|'.$high.'|'.$low.'|'.$close.'|'.$change.'|'.\
$changep.'|'.$price.'|'.$delay.'|'.$stock_count.'|'.$stock_initvalue;;\
\
last;;\
}\
}\
\
sleep(10);;\
};;\
if (!$@)\
{\
print $@;;\
last;;\
}\
}\
\
sleep(10);;\
}\
}\
\
$output = '|||||||||||||||||||||' if ($output eq '');; \
\
return $name.$output;;\
}\
\
sub DOIF::endUpdate($)\
{\
my ($name, @output) = split("\\|", shift);;\
my $hash = $::defs{$name};;\
my $numValues = 21;;\
\
#::Log3 $name, 1, $name.': '.Dumper(\@output);;\
\
if (0 != (scalar(@output) % $numValues))\
{\
::Log3 $name, 1, $name.': $numShares does not match the received output.';;\
}\
else\
{ \
my $totalValue = 0;;\
my $totalInitValue = 0;;\
my $totalCloseValue = 0;;\
## using $_stocks is not reliable\
my @stocks = split(",", ::AttrVal($name, 'stocks', ''));;\
\
::readingsBeginUpdate($hash);;\
for (my $i = 0;; $i < (scalar(@output) / $numValues);; $i += 1)\
{\
my $pos = $i * $numValues;;\
my $prefix = $output[$pos];;\
\
## remove any .\
$prefix =~ s/\.//g;;\
\
## preserve some values if we do not get new ones (e.g. in case of weekend)\
::readingsBulkUpdate($hash, $prefix.'_unique_symbol', $output[$pos]);;\
::readingsBulkUpdate($hash, $prefix.'_symbol', $output[$pos + 1]);;\
::readingsBulkUpdate($hash, $prefix.'_shortname', $output[$pos + 2]);;\
::readingsBulkUpdate($hash, $prefix.'_longname', $output[$pos + 3]);;\
::readingsBulkUpdate($hash, $prefix.'_type', $output[$pos + 4]);;\
::readingsBulkUpdate($hash, $prefix.'_currency', $output[$pos + 5]);;\
::readingsBulkUpdate($hash, $prefix.'_exchangeSymbol', $output[$pos + 6]);;\
::readingsBulkUpdate($hash, $prefix.'_exchangeName', $output[$pos + 7]);;\
::readingsBulkUpdate($hash, $prefix.'_marketState', $output[$pos + 8]);;\
::readingsBulkUpdate($hash, $prefix.'_timestamp', looks_like_number($output[$pos + 9]) ? localtime($output[$pos + 9]) : $output[$pos + 9]);;\
::readingsBulkUpdate($hash, $prefix.'_volume', $output[$pos + 10]) if ($output[$pos + 10] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_open', $output[$pos + 11]) if ($output[$pos + 11] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_high', $output[$pos + 12]) if ($output[$pos + 12] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_low', $output[$pos + 13]) if ($output[$pos + 13] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_close', $output[$pos + 14]) if ($output[$pos + 14] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_change', $output[$pos + 15]) if ($output[$pos + 15] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_changep', $output[$pos + 16]) if ($output[$pos + 16] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_price', $output[$pos + 17]);;\
::readingsBulkUpdate($hash, $prefix.'_delay', $output[$pos + 18]);;\
::readingsBulkUpdate($hash, $prefix.'_count', $output[$pos + 19]);;\
::readingsBulkUpdate($hash, $prefix.'_initvalue', $output[$pos + 20]);;\
}\
::readingsEndUpdate($hash, 1);;\
\
if (scalar(@stocks) == 0)\
{\
#::Log3 $name, 1, $name.': stocks = '.$_stocks;;\
#::Log3 $name, 1, $name.': stocks = '.::AttrVal($name, 'stocks', 'test');;;;\
@stocks = split(",", ::AttrVal($name, 'stocks', ''));;\
}\
\
foreach my $stock (@stocks)\
{\
my ($stock_symbol, $stock_count, $stock_initvalue) = split(':', $stock);;\
my ($stock_symbol_main, $stock_symbol_alt) = split('\|', $stock_symbol);;\
\
## remove any .\
$stock_symbol_main =~ s/\.//g;;\
\
$stock_count = 1 if (!defined($stock_count));;\
$stock_initvalue = 0 if (!defined($stock_initvalue));;\
\
$totalValue += ($stock_count * ReadingsVal($name, $stock_symbol_main.'_price', 0));;\
$totalInitValue += $stock_initvalue;;\
$totalCloseValue += ($stock_count * ReadingsVal($name, $stock_symbol_main.'_close', 0));;\
}\
\
$totalValue = 1 if (0 == $totalValue);;\
::readingsBeginUpdate($hash);;\
::readingsBulkUpdate($hash, 'totalValue', $totalValue);;\
::readingsBulkUpdate($hash, 'totalInitValue', $totalInitValue);;\
::readingsBulkUpdate($hash, 'totalCloseValue', $totalCloseValue);;\
::readingsBulkUpdate($hash, 'numberOfLines', scalar(@stocks));;\
::readingsEndUpdate($hash, 1);;\
}\
\
delete($_blockingcalls{'PID_UPDATE'.$name});;\
\
::Log3 $name, 4, $name.': Blocking call finished to update shares.';;\
}\
\
sub DOIF::abortUpdate($)\
{\
my $name = shift;;\
\
delete($_blockingcalls{'PID_UPDATE'.$name});;\
\
::Log3 $name, 1, $name.': Blocking call aborted (update).';;\
}\
}\
init\
{\
startUpdate("$SELF");;\
}\
## start in a raster of 30min\
{[08:00-22:30,+:30|12345];;startUpdate("$SELF")}
attr doif_Stocks userattr stocks api:YahooJSON1,YahooJSON2,S-InvestorXETRA,S-InvestorTRADEGATE
attr doif_Stocks api YahooJSON2
attr doif_Stocks disable 0
attr doif_Stocks event-on-change-reading .*
attr doif_Stocks room FINANCE
attr doif_Stocks stocks US02079K3059|GOOGL:100:8000,US5949181045|MSFT:100:40000
attr doif_Stocks uiTable {\
package ui_Table;;\
\
$SHOWNOSTATE = 1;;\
$ATTRIBUTESFIRST = 1;;\
\
{\
my $numberOfLines = ::ReadingsNum("$SELF", "numberOfLines", 0) + 1;;\
\
## Header\
$TR{0} = "style='color:yellow;;text-align:left;;font-weight:bold;;font-size:18px;;border-bottom-style:solid;;border-color:#CCCCCC;;border-bottom-width:1px;;'";;\
## Footer\
$TR{3} = "style='color:yellow;;text-align:left;;font-weight:bold;;font-size:18px;;border-top-style:solid;;border-color:#CCCCCC;;border-top-width:1px;;'";;\
## First 2 columns\
$TD{0..3}{0..1} = "style='font-size:16px;;border-right-style:solid;;border-color:#CCCCCC;;border-right-width:1px;;'";;\
## Column 2 to 7\
$TD{0..3}{2..7} = "align='right' style='font-size:16px;;border-right-style:solid;;border-color:#CCCCCC;;border-right-width:1px;;'";;\
## Column 8\
$TD{0..3}{8} = "align='right' style='font-size:16px'";;\
}\
\
sub totalValue\
{\
if (($_[0] * $_[1]) == 0)\
{\
return 0.0001;;\
}\
else\
{\
return ($_[0] * $_[1]);;\
}\
}\
\
sub getChangeColor\
{\
if ($_[0] >= 5.0)\
{\
return "<div style='color:darkgreen;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 3.0)\
{\
return "<div style='color:green;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 0.0)\
{\
return "<div style='color:lightgreen;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= -1.0)\
{\
return "<div style='color:orange;;'>$_[0]%</div>";;\
}\
else\
{\
return "<div style='color:red;;'>$_[0]%</div>";;\
}\
}\
\
sub getTotalChangeColor\
{\
if ($_[0] >= 5.0)\
{\
return "<div style='color:darkgreen;;font-weight:bold;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 3.0)\
{\
return "<div style='color:green;;font-weight:bold;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 0.0)\
{\
return "<div style='color:lightgreen;;font-weight:bold;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= -1.0)\
{\
return "<div style='color:orange;;font-weight:bold;;'>$_[0]%</div>";;\
}\
else\
{\
return "<div style='color:red;;font-weight:bold;;'>$_[0]%</div>";;\
}\
}\
\
sub getTrendColor\
{\
if ($_[0] >= 1.0)\
{\
return "<div style='color:darkgreen;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 0.0)\
{\
return "<div style='color:green;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= -1.0)\
{\
return "<div style='color:yellow;;'>$_[0]%</div>";;\
}\
else\
{\
return "<div style='color:red;;'>$_[0]%</div>";;\
}\
}\
\
sub getTotalTrendColor\
{\
if ($_[0] >= 1.0)\
{\
return "<div style='color:darkgreen;;font-weight:bold;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 0.0)\
{\
return "<div style='color:green;;font-weight:bold;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= -1.0)\
{\
return "<div style='color:yellow;;font-weight:bold;;'>$_[0]%</div>";;\
}\
else\
{\
return "<div style='color:red;;font-weight:bold;;'>$_[0]%</div>";;\
}\
}\
}\
\
DEF TPL_STOCK([$1:$2_unique_symbol]|[$1:$2_shortname]|sprintf("%.2f",totalValue([$1:$2_count],[$1:$2_price]))."€"|sprintf("%.2f",(totalValue([$1:$2_count],[$1:$2_price])-[$1:$2_initvalue]))."€"|getChangeColor(sprintf("%.2f",100*((totalValue([$1:$2_count],[$1:$2_price]))-[$1:$2_initvalue])/(totalValue([$1:$2_count],[$1:$2_price]))))|getTrendColor(sprintf("%.2f",[$1:$2_changep]))|sprintf("%.2f",[$1:$2_price])."€"|[$1:$2_count])\
\
"Symbol"|"Name"|"Total Value"|"Change abs."|"Change rel."|"Trend"|"Value"|"Count"\
TPL_STOCK($SELF,US02079K3059)\
TPL_STOCK($SELF,US5949181045)\
" ;"|" ;"|sprintf("%.2f",[$SELF:totalValue])."€"|sprintf("%.2f",[$SELF:totalValue]-[$SELF:totalInitValue])."€"|getTotalChangeColor(sprintf("%.2f",([$SELF:totalValue]-[$SELF:totalInitValue])*100/[$SELF:totalValue]))|getTotalTrendColor(sprintf("%.2f",([$SELF:totalValue]-[$SELF:totalCloseValue])*100/[$SELF:totalValue]))|" ;"|" ;"\
Das Attribut stocks setzt sich wie folgt zusammen (mehrere Angaben müssen mit einem | getrennt werden):
<symbol>|<alternatives symbol>:<anzahl>:<initialwert aller aktien zusammengerechnet>
Das Symbol muss für Abfragen über die Yahoo API über Yahoo ermittelt werden. Für die beiden S-Invester APIs muss hingegen die ISIN verwendet werden!
Über das Attribut api kann die Art der Abfrage festgelegt werden.
Ein Update kann länger dauern, je nachdem wie viele Aktien man eingetragen hat. Jeder Aufruf wird um 10s verzögert, damit man nicht geblockt wird!
Eine Anzeige der Charts kann man exemplarisch hiermit machen:
defmod doif_Stocks_Charts DOIF ##
attr doif_Stocks_Charts room FINANCE
attr doif_Stocks_Charts uiTable {\
package ui_Table;;\
}\
\
card([doif_Stocks:<prefix der Aktie>_price:col4w],"Name der Aktie",undef,0,100,0,120,"€",undef,"1","200,,,,,1")
Ausgehend vom Initialwert einer Einzelaktie setze ich bei mir den unteren Wert immer auf Initialwert - 20% und den oberen Wert auf den Initialwert + 20%. Dann kann man anhand der Farbe immer schön sehen, wohin die das Wertpapier, ausgehend von seinem Initialwert, entwickelt.
Jetzt fehlt noch die Kursentwicklung als Diagramm :)
bring ihn nicht auf komische Ideen...
Ich hab grad mitbekommen, das Yahoo seit gestern deutsche IP's blockt und die Abfragen nur noch 404 zurück liefern. Ich glaube ich muss erst mal eine zuverlässigere Datenquelle suchen.
Wenn das also momentan nicht funktioniert, dann liegt das nicht an meinem Code.
Ich habe eine andere API gefunden, die hoffentlich länger funktioniert. Ich bin auch nicht sicher, ob nur meine IP geblockt wurde oder es ein generelles Problem ist.
Zitat von: Damian am 13 Juli 2023, 14:03:25Jetzt fehlt noch die Kursentwicklung als Diagramm :)
Check. Siehe Oben...
Zitat von: mumpitzstuff am 14 Juli 2023, 21:05:04Zitat von: Damian am 13 Juli 2023, 14:03:25Jetzt fehlt noch die Kursentwicklung als Diagramm :)
Check. Siehe Oben...
schön, ist ja mit einer Zeile erledigt :)
Verschiedene Bugs wurden behoben und 4 APIs wurden eingefügt, die über ein Attribut umschaltbar sind (2x Yahoo, 1x S-Investor XETRA, 1x S-Investor Tradegate).
Gefällt mir gut dein DOIF.
Aber wenn man mehr als 2 Aktien eingibt ist die Darstellung leider nicht mehr so schön.
Kann man den style entsprechend anpassen?
Gruß
Hubert
Bei den markierten Stellen im Attribut uiTable muss bei 2 Aktien eine 3 stehen, bei 3 Aktien eine 4 usw., also immer Anzahl der Aktien + 1.
Man könnte es mit Variablen versuchen, leider werden diese nicht immer genommen (siehe TV DOIF), ein System konnte ich nicht erkennen.
Ich habe es mal so versucht:
in startUpdate:
my $length = @stocks;
set_Reading("Anzahl", $length);
in uiTable
my $anzahl = ::ReadingsNum("$SELF","Anzahl",0) + 1;;
## Header
$TR{0} = "style='color:blue;text-align:left;font-weight:bold;font-size:18px;border-bottom-style:solid;border-color:#CCCCCC;border-bottom-width:1px;'";
## Footer
$TR{$anzahl} = "style='color:blue;text-align:left;font-weight:bold;font-size:18px;border-top-style:solid;border-color:#CCCCCC;border-top-width:1px;'";
## First 2 columns
$TD{0..$anzahl}{0..1} = "style='font-size:16px;border-right-style:solid;border-color:#CCCCCC;border-right-width:1px;'";
## Column 2 to 7
$TD{0..$anzahl}{2..7} = "align='right' style='font-size:16px;border-right-style:solid;border-color:#CCCCCC;border-right-width:1px;'";
## Column 8
$TD{0..$anzahl}{8} = "align='right' style='font-size:16px'";
Scheint zu funktionieren.
Gruß
Hubert
Vielen Dank! Ich schaue es mir an und nehme das mit auf, falls es auch bei mir funktioniert.
Leider hat es bei mir nur bei dem einen DOIF funktioniert, bei dem anderen aber nicht. Dort musste ich mehrmals irgendwo Leerzeichen in der uiTable einfügen und speichern und dann auf einmal hat es funktioniert. Das ist das selbe Problem wie ich es beim Fernsehprogramm DOIF hatte und wie oben schon erwähnt wurde. Wenn es funktioniert dann ist es schön, aber leider tut es das nicht immer...
Vllt mal ne Anfrage an den Web-Cheffe?
Ich habe jetzt mal eine Version fertig gemacht mit dem Vorschlag von Damien damals (ich hoffe ich habe es rihtig verstanden).
defmod doif_Stocks DOIF subs\
{\
use utf8;;\
use Blocking;;\
use Scalar::Util qw(looks_like_number);;\
use LWP::UserAgent;;\
use HTTP::Request::Common;;\
use JSON qw(decode_json);;\
use HTML::Entities;;\
use HTML::TreeBuilder;;\
use Web::Scraper;;\
use Data::Dumper qw(Dumper);;\
\
$_stocks = AttrVal("$SELF", 'stocks', '');;\
$_api = AttrVal("$SELF", 'api', 'YahooJSON1');;\
\
sub startUpdate($)\
{\
my $name = shift;;\
$_stocks = AttrVal($name, 'stocks', '');;\
$_api = AttrVal($name, 'api', 'YahooJSON1');;\
my @stocks = split(',', $_stocks);;\
## max 3 retries (always wait 10s between each request)\
my $timeout = (scalar(@stocks) * 65) + 60;;\
\
::Log3 $name, 5, $name.': stocks = '.$_stocks;;\
::Log3 $name, 5, $name.': api = '.$_api;;\
\
if (defined($_blockingcalls{'PID_UPDATE'.$name}))\
{\
::Log3 $name, 3, $name.': Blocking call already running (update).';;\
\
::BlockingKill($_blockingcalls{'PID_UPDATE'.$name});;\
}\
\
::Log3 $name, 3, $name.': Blocking call started (update).';;\
\
$_blockingcalls{'PID_UPDATE'.$name} = ::BlockingCall('DOIF::doUpdate', $name, 'DOIF::endUpdate', $timeout, 'DOIF::abortUpdate', $name);;\
}\
\
sub DOIF::doUpdate($)\
{\
my $name = shift;;\
my $output = '';;\
my $url_template = '';; \
my @stocks = split(',', $_stocks);;\
\
if ($_api eq 'YahooJSON1')\
{\
$url_template = 'https://query2.finance.yahoo.com/v7/finance/options/#REPLACE#';; \
} \
elsif ($_api eq 'YahooJSON2')\
{\
$url_template = 'https://query2.finance.yahoo.com/v11/finance/quoteSummary/#REPLACE#?modules=price';;\
}\
elsif ($_api eq 'S-InvestorXETRA')\
{\
$url_template = 'https://web.s-investor.de/app/detail.htm?INST_ID=0003329&boerse=GER&isin=#REPLACE#';;\
}\
elsif ($_api eq 'S-InvestorTRADEGATE')\
{\
$url_template = 'https://web.s-investor.de/app/detail.htm?INST_ID=0003329&boerse=TDG&isin=#REPLACE#';;\
}\
\
foreach my $stock (@stocks)\
{\
my ($stock_symbol, $stock_count, $stock_initvalue) = split(':', $stock);;\
my $url = $url_template;;\
$url =~ s/#REPLACE#/$stock_symbol/;;\
my $ua = LWP::UserAgent->new;;\
my $reply;;\
\
$ua->agent('Mozilla/5.0 (Windows NT 10.0;; Win64;; x64;; rv:109.0) Gecko/20100101 Firefox/115.0');;\
\
## retry up to 3 times\
for (my $i = 0;; $i < 3;; $i++)\
{\
if (($_api eq 'YahooJSON1') || ($_api eq 'YahooJSON2'))\
{ \
$reply = $ua->request(GET $url);;\
\
if (200 == $reply->code)\
{\
my $json_data = JSON::decode_json $reply->content;;\
my $json_data_count;;\
$json_data_count = scalar @{ $json_data->{'optionChain'}{'result'} } if ($_api eq 'YahooJSON1');;\
$json_data_count = scalar @{ $json_data->{'quoteSummary'}{'result'} } if ($_api eq 'YahooJSON2');;\
\
if ($json_data_count >= 1)\
{\
my $json_resources_price;;\
my $json_volume;;\
my $json_open;;\
my $json_high;;\
my $json_low;;\
my $json_close;;\
my $json_change;;\
my $json_changep;;\
my $json_price;; \
\
$json_resources_price = $json_data->{'optionChain'}{'result'}[0]{'quote'} if ($_api eq 'YahooJSON1');;\
$json_resources_price = $json_data->{'quoteSummary'}{'result'}[0]{'price'} if ($_api eq 'YahooJSON2');;\
my $json_symbol = (exists($json_resources_price->{'symbol'}) ? $json_resources_price->{'symbol'} : '');;\
my $json_shortname = (exists($json_resources_price->{'shortName'}) ? $json_resources_price->{'shortName'} : '');;\
my $json_longname = (exists($json_resources_price->{'longName'}) ? $json_resources_price->{'longName'} : '');;\
my $json_type = (exists($json_resources_price->{'quoteType'}) ? $json_resources_price->{'quoteType'} : '');;\
my $json_currency = (exists($json_resources_price->{'currency'}) ? $json_resources_price->{'currency'} : '');;\
my $json_exchangeSymbol = (exists($json_resources_price->{'exchange'}) ? $json_resources_price->{'exchange'} : '');;\
my $json_exchangeName = (exists($json_resources_price->{'exchangeName'}) ? $json_resources_price->{'exchangeName'} : '');;\
my $json_delay = (exists($json_resources_price->{'exchangeDataDelayedBy'}) ? $json_resources_price->{'exchangeDataDelayedBy'} : '');;\
my $json_marketState = (exists($json_resources_price->{'marketState'}) ? $json_resources_price->{'marketState'} : '');;\
my $json_timestamp = (exists($json_resources_price->{'regularMarketTime'}) ? $json_resources_price->{'regularMarketTime'} : '');;\
if ($_api eq 'YahooJSON1')\
{\
$json_volume = (exists($json_resources_price->{'regularMarketVolume'}) ? $json_resources_price->{'regularMarketVolume'} : '');;\
$json_open = (exists($json_resources_price->{'regularMarketOpen'}) ? $json_resources_price->{'regularMarketOpen'} : '');;\
$json_high = (exists($json_resources_price->{'regularMarketDayHigh'}) ? $json_resources_price->{'regularMarketDayHigh'} : '');;\
$json_low = (exists($json_resources_price->{'regularMarketDayLow'}) ? $json_resources_price->{'regularMarketDayLow'} : '');;\
$json_close = (exists($json_resources_price->{'regularMarketPreviousClose'}) ? $json_resources_price->{'regularMarketPreviousClose'} : '');;\
$json_change = (exists($json_resources_price->{'regularMarketChange'}) ? $json_resources_price->{'regularMarketChange'} : '');;\
$json_changep = (exists($json_resources_price->{'regularMarketChangePercent'}) ? $json_resources_price->{'regularMarketChangePercent'} : '');;\
$json_price = (exists($json_resources_price->{'regularMarketPrice'}) ? $json_resources_price->{'regularMarketPrice'} : '');;\
}\
elsif ($_api eq 'YahooJSON2')\
{\
$json_volume = (exists($json_resources_price->{'regularMarketVolume'}) && exists($json_resources_price->{'regularMarketVolume'}{'raw'}) ?\
$json_resources_price->{'regularMarketVolume'}{'raw'} : '');;\
$json_open = (exists($json_resources_price->{'regularMarketOpen'}) && exists($json_resources_price->{'regularMarketOpen'}{'raw'}) ?\
$json_resources_price->{'regularMarketOpen'}{'raw'} : '');;\
$json_high = (exists($json_resources_price->{'regularMarketDayHigh'}) && exists($json_resources_price->{'regularMarketDayHigh'}{'raw'}) ?\
$json_resources_price->{'regularMarketDayHigh'}{'raw'} : '');;\
$json_low = (exists($json_resources_price->{'regularMarketDayLow'}) && exists($json_resources_price->{'regularMarketDayLow'}{'raw'}) ?\
$json_resources_price->{'regularMarketDayLow'}{'raw'} : '');;\
$json_close = (exists($json_resources_price->{'regularMarketPreviousClose'}) && exists($json_resources_price->{'regularMarketPreviousClose'}{'raw'}) ?\
$json_resources_price->{'regularMarketPreviousClose'}{'raw'} : '');;\
$json_change = (exists($json_resources_price->{'regularMarketChange'}) && exists($json_resources_price->{'regularMarketChange'}{'raw'}) ?\
$json_resources_price->{'regularMarketChange'}{'raw'} : '');;\
$json_changep = (exists($json_resources_price->{'regularMarketChangePercent'}) && exists($json_resources_price->{'regularMarketChangePercent'}{'raw'}) ?\
$json_resources_price->{'regularMarketChangePercent'}{'raw'} : '');;\
$json_price = (exists($json_resources_price->{'regularMarketPrice'}) && exists($json_resources_price->{'regularMarketPrice'}{'raw'}) ?\
$json_resources_price->{'regularMarketPrice'}{'raw'} : '');;\
}\
\
## replace | by _\
$json_longname = decode_entities($json_longname);;\
$json_shortname =~ s/\|/_/g;;\
$json_longname =~ s/\|/_/g;;\
\
## convert to %\
$json_changep = $json_changep * 100 if (($json_changep ne '') && ($_api eq 'YahooJSON2'));;\
\
$stock_count = 1 if (!defined($stock_count));;\
$stock_initvalue = 0 if (!defined($stock_initvalue));;\
\
\
if (($json_currency eq "GBp") ||\
($json_currency eq "GBX") ||\
($json_currency eq "ZAc"))\
{\
$json_price = $json_price / 100 if ($json_price ne '');;\
$json_open = $json_open / 100 if ($json_open ne '');;\
$json_high = $json_high / 100 if ($json_high ne '');;\
$json_low = $json_low / 100 if ($json_low ne '');;\
$json_close = $json_close / 100 if ($json_close ne '');;\
$json_change = $json_change / 100 if ($json_change ne '');;\
}\
\
if (($json_currency eq "GBp") ||\
($json_currency eq "GBX"))\
{\
$json_currency = "GBP";;\
}\
elsif ($json_currency eq "ZAc")\
{\
$json_currency = "ZAR";;\
}\
\
$output .= '|'.$json_symbol.'|'.$json_shortname.'|'.$json_longname.'|'.$json_type.'|'.$json_currency.'|'.\
$json_exchangeSymbol.'|'.$json_exchangeName.'|'.$json_marketState.'|'.$json_timestamp.'|'.\
$json_volume.'|'.$json_open.'|'.$json_high.'|'.$json_low.'|'.$json_close.'|'.$json_change.'|'.\
$json_changep.'|'.$json_price.'|'.$json_delay.'|'.$stock_count.'|'.$stock_initvalue;;\
\
sleep(10);;\
last;;\
}\
}\
}\
elsif (($_api eq 'S-InvestorXETRA') || ($_api eq 'S-InvestorTRADEGATE'))\
{\
my $type = '';; \
my $exchangeSymbol;;\
my $delay = '';;\
my $marketState = '';;\
\
$exchangeSymbol = 'GER' if ($_api eq 'S-InvestorXETRA');;\
$exchangeSymbol = 'TDG' if ($_api eq 'S-InvestorTRADEGATE');;\
\
eval\
{\
my $tree = HTML::TreeBuilder->new_from_url($url);;\
\
my $lastvalue = $tree->look_down('class'=>'si_inner_content_box');;\
\
my $td1 = ($lastvalue->look_down('_tag'=>'td'))[1];;\
my @child = $td1->content_list;;\
my $symbol = $child[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[3];;\
@child = $td1->content_list;;\
my $shortname = $child[0];;\
my $longname = $shortname;;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[5];;\
@child = $td1->content_list;;\
my $exchangeName = $child[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[7];;\
@child = $td1->content_list;;\
my $timestamp = substr($child[0], 0, 8).' '.substr($child[0], 9, 5);;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[9];;\
@child = $td1->content_list;;\
my $price = $child[0];;\
$price =~ s/\.//g;;\
$price =~ s/,/\./;;\
my $encprice = encode_entities($price);;\
my @splitprice = split ('&', $encprice);;\
$price = $splitprice[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[11];;\
@child = $td1->content_list;;\
my $currency = $child[0];;\
$currency =~ s/Euro/EUR/;;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[13];;\
@child = $td1->content_list;;\
my $volume = $child[0];;\
\
$lastvalue = $tree->look_down('id'=>'detailVergleichszahlen');;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[7];;\
@child = $td1->content_list;;\
my $open = $child[0];;\
$open =~ s/\.//g;;\
$open =~ s/,/\./;;\
my $encopen = encode_entities($open);;\
my @splitopen = split ('&', $encopen);;\
$open = $splitopen[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[10];;\
@child = $td1->content_list;;\
my $high = $child[0];;\
$high =~ s/\.//g;;\
$high =~ s/,/\./;;\
my $enchigh = encode_entities($high);;\
my @splithigh = split ('&', $enchigh);;\
$high = $splithigh[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[13];;\
@child = $td1->content_list;;\
my $low = $child[0];;\
$low =~ s/\.//g;;\
$low =~ s/,/\./;;\
my $enclow = encode_entities($low);;\
my @splitlow = split ('&', $enclow);;\
$low = $splitlow[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[16];;\
@child = $td1->content_list;;\
my $change = $child[0];;\
$change =~ s/\.//g;;\
$change =~ s/,/\./;;\
my $encchange = encode_entities($change);;\
my @splitchange = split ('&', $encchange);;\
$change = $splitchange[0];;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[19];;\
@child = $td1->content_list;;\
my $changep = $child[0];;\
$changep =~ s/[\.|%]//g;;\
$changep =~ s/,/\./;;\
\
$td1 = ($lastvalue->look_down('_tag'=>'td'))[37];;\
@child = $td1->content_list;;\
my $close = $child[0];;\
$close =~ s/\.//g;;\
$close =~ s/,/\./;;\
my $encclose = encode_entities($close);;\
my @splitclose = split ('&', $encclose);;\
$close = $splitclose[0];;\
\
## replace | by _\
$shortname =~ s/\|/_/g;;\
$longname =~ s/(\||&;)/_/g;;\
\
$stock_count = 1 if (!defined($stock_count));;\
$stock_initvalue = 0 if (!defined($stock_initvalue));;\
\
$output .= '|'.$symbol.'|'.$shortname.'|'.$longname.'|'.$type.'|'.$currency.'|'.\
$exchangeSymbol.'|'.$exchangeName.'|'.$marketState.'|'.$timestamp.'|'.\
$volume.'|'.$open.'|'.$high.'|'.$low.'|'.$close.'|'.$change.'|'.\
$changep.'|'.$price.'|'.$delay.'|'.$stock_count.'|'.$stock_initvalue;;\
\
sleep(10);;\
};;\
last if (!$@);;\
}\
\
sleep(10);;\
}\
}\
\
$output = '||||||||||||||||||||' if ($output eq '');; \
\
return $name.$output;;\
}\
\
sub DOIF::endUpdate($)\
{\
my ($name, @output) = split("\\|", shift);;\
my $hash = $::defs{$name};;\
my $numValues = 20;;\
\
::Log3 $name, 5, $name.': '.Dumper(\@output);;\
\
if (0 != (scalar(@output) % $numValues))\
{\
::Log3 $name, 1, $name.': $numShares does not match the received output.';;\
}\
else\
{ \
my $totalValue = 0;;\
my $totalInitValue = 0;;\
my $totalCloseValue = 0;;\
my @stocks = split(',', $_stocks);;\
\
::readingsBeginUpdate($hash);;\
for (my $i = 0;; $i < (scalar(@output) / $numValues);; $i += 1)\
{\
my $pos = $i * $numValues;;\
my $prefix = $output[$pos];;\
\
## remove any .\
$prefix =~ s/\.//g;;\
\
## preserve some values if we do not get new ones (e.g. in case of weekend)\
::readingsBulkUpdate($hash, $prefix.'_symbol', $output[$pos]);;\
::readingsBulkUpdate($hash, $prefix.'_shortname', $output[$pos + 1]);;\
::readingsBulkUpdate($hash, $prefix.'_longname', $output[$pos + 2]);;\
::readingsBulkUpdate($hash, $prefix.'_type', $output[$pos + 3]);;\
::readingsBulkUpdate($hash, $prefix.'_currency', $output[$pos + 4]);;\
::readingsBulkUpdate($hash, $prefix.'_exchangeSymbol', $output[$pos + 5]);;\
::readingsBulkUpdate($hash, $prefix.'_exchangeName', $output[$pos + 6]);;\
::readingsBulkUpdate($hash, $prefix.'_marketState', $output[$pos + 7]);;\
::readingsBulkUpdate($hash, $prefix.'_timestamp', looks_like_number($output[$pos + 8]) ? localtime($output[$pos + 8]) : $output[$pos + 8]);;\
::readingsBulkUpdate($hash, $prefix.'_volume', $output[$pos + 9]) if ($output[$pos + 9] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_open', $output[$pos + 10]) if ($output[$pos + 10] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_high', $output[$pos + 11]) if ($output[$pos + 11] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_low', $output[$pos + 12]) if ($output[$pos + 12] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_close', $output[$pos + 13]) if ($output[$pos + 13] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_change', $output[$pos + 14]) if ($output[$pos + 14] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_changep', $output[$pos + 15]) if ($output[$pos + 15] ne '');;\
::readingsBulkUpdate($hash, $prefix.'_price', $output[$pos + 16]);;\
::readingsBulkUpdate($hash, $prefix.'_delay', $output[$pos + 17]);;\
::readingsBulkUpdate($hash, $prefix.'_count', $output[$pos + 18]);;\
::readingsBulkUpdate($hash, $prefix.'_initvalue', $output[$pos + 19]);;\
}\
::readingsEndUpdate($hash, 1);;\
\
foreach my $stock (@stocks)\
{\
my ($stock_symbol, $stock_count, $stock_initvalue) = split(':', $stock);;\
\
## remove any .\
$stock_symbol =~ s/\.//g;;\
\
$stock_count = 1 if (!defined($stock_count));;\
$stock_initvalue = 0 if (!defined($stock_initvalue));;\
\
$totalValue += ($stock_count * ReadingsVal($name, $stock_symbol.'_price', 0));;\
$totalInitValue += $stock_initvalue;;\
$totalCloseValue += ($stock_count * ReadingsVal($name, $stock_symbol.'_close', 0));;\
}\
\
::readingsBeginUpdate($hash);;\
::readingsBulkUpdate($hash, 'totalValue', $totalValue);;\
::readingsBulkUpdate($hash, 'totalInitValue', $totalInitValue);;\
::readingsBulkUpdate($hash, 'totalCloseValue', $totalCloseValue);;\
::readingsBulkUpdate($hash, 'numberOfLines', scalar(@stocks));;\
::readingsEndUpdate($hash, 1);;\
}\
\
delete($_blockingcalls{'PID_UPDATE'.$name});;\
\
::Log3 $name, 4, $name.': Blocking call finished to update shares.';;\
}\
\
sub DOIF::abortUpdate($)\
{\
my $name = shift;;\
\
delete($_blockingcalls{'PID_UPDATE'.$name});;\
\
::Log3 $name, 1, $name.': Blocking call aborted (update).';;\
}\
}\
init\
{\
startUpdate("$SELF");;\
}\
## start in a raster of 30min\
{[08:00-22:30,+:30|12345];;startUpdate("$SELF")}
attr doif_Stocks userattr stocks api:YahooJSON1,YahooJSON2,S-InvestorXETRA,S-InvestorTRADEGATE
attr doif_Stocks api S-InvestorXETRA
attr doif_Stocks disable 0
attr doif_Stocks event-on-change-reading .*
attr doif_Stocks room FINANCE
attr doif_Stocks stocks US02079K3059:100:8000,US5949181045:100:40000
attr doif_Stocks uiTable {\
package ui_Table;;\
\
$SHOWNOSTATE = 1;;\
$ATTRIBUTESFIRST = 1;;\
\
{\
my $numberOfLines = ::ReadingsNum("$SELF", "numberOfLines", 0) + 1;;\
\
## Header\
$TR{0} = "style='color:yellow;;text-align:left;;font-weight:bold;;font-size:18px;;border-bottom-style:solid;;border-color:#CCCCCC;;border-bottom-width:1px;;'";;\
## Footer\
$TR{$numberOfLines} = "style='color:yellow;;text-align:left;;font-weight:bold;;font-size:18px;;border-top-style:solid;;border-color:#CCCCCC;;border-top-width:1px;;'";;\
## First 2 columns\
$TD{0..$numberOfLines}{0..1} = "style='font-size:16px;;border-right-style:solid;;border-color:#CCCCCC;;border-right-width:1px;;'";;\
## Column 2 to 7\
$TD{0..$numberOfLines}{2..7} = "align='right' style='font-size:16px;;border-right-style:solid;;border-color:#CCCCCC;;border-right-width:1px;;'";;\
## Column 8\
$TD{0..$numberOfLines}{8} = "align='right' style='font-size:16px'";;\
}\
\
sub totalValue\
{\
return ($_[0] * $_[1]);;\
}\
\
sub getChangeColor\
{\
if ($_[0] >= 5.0)\
{\
return "<div style='color:darkgreen;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 3.0)\
{\
return "<div style='color:green;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 0.0)\
{\
return "<div style='color:lightgreen;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= -1.0)\
{\
return "<div style='color:orange;;'>$_[0]%</div>";;\
}\
else\
{\
return "<div style='color:red;;'>$_[0]%</div>";;\
}\
}\
\
sub getTotalChangeColor\
{\
if ($_[0] >= 5.0)\
{\
return "<div style='color:darkgreen;;font-weight:bold;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 3.0)\
{\
return "<div style='color:green;;font-weight:bold;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 0.0)\
{\
return "<div style='color:lightgreen;;font-weight:bold;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= -1.0)\
{\
return "<div style='color:orange;;font-weight:bold;;'>$_[0]%</div>";;\
}\
else\
{\
return "<div style='color:red;;font-weight:bold;;'>$_[0]%</div>";;\
}\
}\
\
sub getTrendColor\
{\
if ($_[0] >= 1.0)\
{\
return "<div style='color:darkgreen;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 0.0)\
{\
return "<div style='color:green;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= -1.0)\
{\
return "<div style='color:yellow;;'>$_[0]%</div>";;\
}\
else\
{\
return "<div style='color:red;;'>$_[0]%</div>";;\
}\
}\
\
sub getTotalTrendColor\
{\
if ($_[0] >= 1.0)\
{\
return "<div style='color:darkgreen;;font-weight:bold;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= 0.0)\
{\
return "<div style='color:green;;font-weight:bold;;'>$_[0]%</div>";;\
}\
elsif ($_[0] >= -1.0)\
{\
return "<div style='color:yellow;;font-weight:bold;;'>$_[0]%</div>";;\
}\
else\
{\
return "<div style='color:red;;font-weight:bold;;'>$_[0]%</div>";;\
}\
}\
}\
\
DEF TPL_STOCK([$1:$2_symbol]|[$1:$2_shortname]|sprintf("%.2f",totalValue([$1:$2_count],[$1:$2_price]))."€"|sprintf("%.2f",(totalValue([$1:$2_count],[$1:$2_price])-[$1:$2_initvalue]))."€"|getChangeColor(sprintf("%.2f",100*((totalValue([$1:$2_count],[$1:$2_price]))-[$1:$2_initvalue])/(totalValue([$1:$2_count],[$1:$2_price]))))|getTrendColor(sprintf("%.2f",[$1:$2_changep]))|sprintf("%.2f",[$1:$2_price])."€"|[$1:$2_count])\
\
"Symbol"|"Name"|"Total Value"|"Change abs."|"Change rel."|"Trend"|"Value"|"Count"\
TPL_STOCK($SELF,US02079K3059)\
TPL_STOCK($SELF,US5949181045)\
" ;"|" ;"|sprintf("%.2f",[$SELF:totalValue])."€"|sprintf("%.2f",[$SELF:totalValue]-[$SELF:totalInitValue])."€"|getTotalChangeColor(sprintf("%.2f",([$SELF:totalValue]-[$SELF:totalInitValue])*100/[$SELF:totalValue]))|getTotalTrendColor(sprintf("%.2f",([$SELF:totalValue]-[$SELF:totalCloseValue])*100/[$SELF:totalValue]))|" ;"|" ;"\
Nur zu Info:
im Block subs bist du automatisch im DOIF package, dort ist es überflüssig, wie in jedem DOIF-Block, das DOIF package voranzustellen, statt:
sub DOIF::doUpdate($)
kann man auch direkt angeben:
sub doUpdate($)
Stimmt. Hab ich ja teilweise auch schon so. Ist irgendwie durchgerutscht. Behebe ich beim nächsten Update. Danke!
Zitat von: Damian am 27 Juli 2023, 10:15:56Nur zu Info:
im Block subs bist du automatisch im DOIF package, dort ist es überflüssig, wie in jedem DOIF-Block, das DOIF package voranzustellen, statt:
sub DOIF::doUpdate($)
kann man auch direkt angeben:
sub doUpdate($)
Das funktioniert leider nicht. Sobald ich das DOIF:: bei einer bzw. allen der von BlockingCall aufgerufenen Funktionen entferne, führt das zum Totalabsturz von FHEM. Das lässt sich danach nur noch über Änderungen an der fhem.cfg wiederbeleben.
Ich habe eine Frage zum Modul. Wenn ich stocks ändere (und ich habe ETFs, die teilweise nur einmal am Tag aktualisiert werden), erscheinen in der Tabelle immer noch die alten Werte. Muss ich initialisieren (init, disable-enable reicht auch nicht) und wenn ja, wie mache ich das?
Welche api verwendest du? Werden die Readings schon nicht aktualisiert? Ein init sollte in keinem Fall notwendig sein. Du könntest auch verbose 5 mal einschalten und gucken was passiert. Ansonsten müsstest du mir mal ein Beispiel geben (ISIN bzw. Symbol), damit ich das selbst prüfen kann.
Am Wochenende holt das DOIF übrigens keine Daten, weil die Börsen da zu haben. Eingestellt ist Montag - Freitag von 8.00-22.30 Uhr. An den anderen Tagen/Zeiten werden keine Daten abgeholt. Das kannst du aber anpassen.
Danke für die Hilfe! Ich habe zwei Tage gewartet, aber keine Veränderung. Anbei das list
Internals:
CFGFN
DEF subs
{
use utf8;
use Blocking;
use Scalar::Util qw(looks_like_number);
use LWP::UserAgent;
use HTTP::Request::Common;
use JSON qw(decode_json);
use HTML::Entities;
use HTML::TreeBuilder;
use Web::Scraper;
use Data::Dumper qw(Dumper);
$_stocks = AttrVal("$SELF", 'stocks', '');
$_api = AttrVal("$SELF", 'api', 'YahooJSON1');
sub startUpdate($)
und dann kommen ja die von mir einfach übernommenen Befehle, weiter
init
{
startUpdate("$SELF");
}
## start in a raster of 30min
{[08:00-22:30,+:30|12345];startUpdate("$SELF")}
FUUID 64c217a3-f33f-1115-31a6-6c5163acb5f27c95
MODEL Perl
NAME DOIF_Stocks
NOTIFYDEV DOIF_Stocks,global
NR 596904
NTFY_ORDER 50-DOIF_Stocks
STATE initialized
TYPE DOIF
VERSION 27452 2023-04-16 11:34:30
eventCount 24
READINGS:
2023-08-02 21:00:10 0P0001HOTVF_change 0
2023-08-02 21:00:10 0P0001HOTVF_changep 0
2023-08-02 21:00:10 0P0001HOTVF_close 42.24
2023-08-02 21:00:10 0P0001HOTVF_count 210
2023-08-02 21:00:10 0P0001HOTVF_currency EUR
2023-08-02 21:00:10 0P0001HOTVF_delay 15
2023-08-02 21:00:10 0P0001HOTVF_exchangeName
2023-08-02 21:00:10 0P0001HOTVF_exchangeSymbol FRA
2023-08-02 21:00:10 0P0001HOTVF_initvalue 1000
2023-08-02 21:00:10 0P0001HOTVF_longname Amundi Funds - Euro Government Bond A2 EUR (C)
2023-08-02 21:00:10 0P0001HOTVF_marketState REGULAR
2023-08-02 21:00:10 0P0001HOTVF_price 42.24
2023-08-02 21:00:10 0P0001HOTVF_shortname Amundi Funds - Euro Government
2023-08-02 21:00:10 0P0001HOTVF_symbol 0P0001HOTV.F
2023-08-02 21:00:10 0P0001HOTVF_timestamp Mon Jul 31 22:00:00 2023
2023-08-02 21:00:10 0P0001HOTVF_type MUTUALFUND
2023-07-29 12:29:36 LU1882473264_change -0.0600
2023-07-29 12:29:36 LU1882473264_changep -0.1400
2023-07-29 12:29:36 LU1882473264_close 42.3000
2023-07-29 12:29:36 LU1882473264_count 210
2023-07-29 12:29:36 LU1882473264_currency EUR
2023-07-29 12:29:36 LU1882473264_delay
2023-07-29 12:29:36 LU1882473264_exchangeName KVG Fondskurse
2023-07-29 12:29:36 LU1882473264_exchangeSymbol GER
2023-07-29 12:29:36 LU1882473264_high -
2023-07-29 12:29:36 LU1882473264_initvalue 1000
2023-07-29 12:29:36 LU1882473264_longname Am.Fds-Amundi Fds Eur.Gov.Bd Act. Nom. A2 EUR (C) oN
2023-07-29 12:29:36 LU1882473264_low -
2023-07-29 12:29:36 LU1882473264_marketState
2023-07-29 12:29:36 LU1882473264_open -
2023-07-29 12:29:36 LU1882473264_price 42.2400
2023-07-29 12:29:36 LU1882473264_shortname Am.Fds-Amundi Fds Eur.Gov.Bd Act. Nom. A2 EUR (C) oN
2023-07-29 12:29:36 LU1882473264_symbol LU1882473264
2023-07-29 12:29:36 LU1882473264_timestamp 28.07.23 -
2023-07-29 12:29:36 LU1882473264_type
2023-07-29 12:29:36 LU1882473264_volume 0
2023-07-27 09:30:21 US02079K3059_change +0.100
2023-07-27 09:30:21 US02079K3059_changep +0.09
2023-07-27 09:30:21 US02079K3059_close 116.920
2023-07-27 09:30:21 US02079K3059_count 100
2023-07-27 09:30:21 US02079K3059_currency EUR
2023-07-27 09:30:21 US02079K3059_delay
2023-07-27 09:30:21 US02079K3059_exchangeName XETRA
2023-07-27 09:30:21 US02079K3059_exchangeSymbol GER
2023-07-27 09:30:21 US02079K3059_high 117.300
2023-07-27 09:30:21 US02079K3059_initvalue 8000
2023-07-27 09:30:21 US02079K3059_longname Alphabet Inc. Reg. Shs Cl. A DL-,001
2023-07-27 09:30:21 US02079K3059_low 116.860
2023-07-27 09:30:21 US02079K3059_marketState
2023-07-27 09:30:21 US02079K3059_open 117.300
2023-07-27 09:30:21 US02079K3059_price 117.020
2023-07-27 09:30:21 US02079K3059_shortname Alphabet Inc. Reg. Shs Cl. A DL-,001
2023-07-27 09:30:21 US02079K3059_symbol US02079K3059
2023-07-27 09:30:21 US02079K3059_timestamp 27.07.23 09:14
2023-07-27 09:30:21 US02079K3059_type
2023-07-27 09:30:21 US02079K3059_volume 4
2023-07-27 09:30:21 US5949181045_change +2.000
2023-07-27 09:30:21 US5949181045_changep +0.66
2023-07-27 09:30:21 US5949181045_close 304.250
2023-07-27 09:30:21 US5949181045_count 100
2023-07-27 09:30:21 US5949181045_currency EUR
2023-07-27 09:30:21 US5949181045_delay
2023-07-27 09:30:21 US5949181045_exchangeName XETRA
2023-07-27 09:30:21 US5949181045_exchangeSymbol GER
2023-07-27 09:30:21 US5949181045_high 306.600
2023-07-27 09:30:21 US5949181045_initvalue 40000
2023-07-27 09:30:21 US5949181045_longname Microsoft Corp. Registered Shares DL-,00000625
2023-07-27 09:30:21 US5949181045_low 305.950
2023-07-27 09:30:21 US5949181045_marketState
2023-07-27 09:30:21 US5949181045_open 306.600
2023-07-27 09:30:21 US5949181045_price 306.250
2023-07-27 09:30:21 US5949181045_shortname Microsoft Corp. Registered Shares DL-,00000625
2023-07-27 09:30:21 US5949181045_symbol US5949181045
2023-07-27 09:30:21 US5949181045_timestamp 27.07.23 09:14
2023-07-27 09:30:21 US5949181045_type
2023-07-27 09:30:21 US5949181045_volume 1
2023-08-02 21:00:00 block_02 executed
2023-07-31 16:34:11 block_init executed
2023-07-29 12:34:52 mode enabled
2023-07-27 09:07:16 state initialized
2023-08-01 22:30:00 timer_01_c02 02.08.2023 08:00:00|12345
2023-08-01 22:30:00 timer_02_c02 02.08.2023 22:30:00|12345
2023-08-02 21:00:00 timer_03_c02 02.08.2023 21:30:00|12345
2023-08-02 21:00:10 totalCloseValue 8870.4
2023-08-02 21:00:10 totalInitValue 1000
2023-08-02 21:00:10 totalValue 8870.4
Regex:
accu:
bar:
barAvg:
collect:
uiTable:
DOIF_Stocks:
DOIF_Stocks_uiTable_c_1_0_0_0:
US02079K3059_symbol ^DOIF_Stocks$:^US02079K3059_symbol:
DOIF_Stocks_uiTable_c_1_1_0_0:
US02079K3059_shortname ^DOIF_Stocks$:^US02079K3059_shortname:
DOIF_Stocks_uiTable_c_1_2_0_0:
US02079K3059_count ^DOIF_Stocks$:^US02079K3059_count:
US02079K3059_price ^DOIF_Stocks$:^US02079K3059_price:
DOIF_Stocks_uiTable_c_1_3_0_0:
US02079K3059_count ^DOIF_Stocks$:^US02079K3059_count:
US02079K3059_initvalue ^DOIF_Stocks$:^US02079K3059_initvalue:
US02079K3059_price ^DOIF_Stocks$:^US02079K3059_price:
DOIF_Stocks_uiTable_c_1_4_0_0:
US02079K3059_count ^DOIF_Stocks$:^US02079K3059_count:
US02079K3059_initvalue ^DOIF_Stocks$:^US02079K3059_initvalue:
US02079K3059_price ^DOIF_Stocks$:^US02079K3059_price:
DOIF_Stocks_uiTable_c_1_5_0_0:
US02079K3059_changep ^DOIF_Stocks$:^US02079K3059_changep:
DOIF_Stocks_uiTable_c_1_6_0_0:
US02079K3059_price ^DOIF_Stocks$:^US02079K3059_price:
DOIF_Stocks_uiTable_c_1_7_0_0:
US02079K3059_count ^DOIF_Stocks$:^US02079K3059_count:
DOIF_Stocks_uiTable_c_2_0_0_0:
US5949181045_symbol ^DOIF_Stocks$:^US5949181045_symbol:
DOIF_Stocks_uiTable_c_2_1_0_0:
US5949181045_shortname ^DOIF_Stocks$:^US5949181045_shortname:
DOIF_Stocks_uiTable_c_2_2_0_0:
US5949181045_count ^DOIF_Stocks$:^US5949181045_count:
US5949181045_price ^DOIF_Stocks$:^US5949181045_price:
DOIF_Stocks_uiTable_c_2_3_0_0:
US5949181045_count ^DOIF_Stocks$:^US5949181045_count:
US5949181045_initvalue ^DOIF_Stocks$:^US5949181045_initvalue:
US5949181045_price ^DOIF_Stocks$:^US5949181045_price:
DOIF_Stocks_uiTable_c_2_4_0_0:
US5949181045_count ^DOIF_Stocks$:^US5949181045_count:
US5949181045_initvalue ^DOIF_Stocks$:^US5949181045_initvalue:
US5949181045_price ^DOIF_Stocks$:^US5949181045_price:
DOIF_Stocks_uiTable_c_2_5_0_0:
US5949181045_changep ^DOIF_Stocks$:^US5949181045_changep:
DOIF_Stocks_uiTable_c_2_6_0_0:
US5949181045_price ^DOIF_Stocks$:^US5949181045_price:
DOIF_Stocks_uiTable_c_2_7_0_0:
US5949181045_count ^DOIF_Stocks$:^US5949181045_count:
DOIF_Stocks_uiTable_c_3_2_0_0:
totalValue ^DOIF_Stocks$:^totalValue:
DOIF_Stocks_uiTable_c_3_3_0_0:
totalInitValue ^DOIF_Stocks$:^totalInitValue:
totalValue ^DOIF_Stocks$:^totalValue:
DOIF_Stocks_uiTable_c_3_4_0_0:
totalInitValue ^DOIF_Stocks$:^totalInitValue:
totalValue ^DOIF_Stocks$:^totalValue:
DOIF_Stocks_uiTable_c_3_5_0_0:
totalCloseValue ^DOIF_Stocks$:^totalCloseValue:
totalValue ^DOIF_Stocks$:^totalValue:
condition:
0
startUpdate("DOIF_Stocks");
1 ::DOIF_time($hash,0,1,$wday,$hms,"12345");startUpdate("DOIF_Stocks")
days:
0 12345
1 12345
2 12345
helper:
NOTIFYDEV DOIF_Stocks,global
event timer_3
globalinit 1
last_timer 3
sleeptimer -1
triggerDev
triggerEvents:
timer_3
triggerEventsState:
timer_3
interval:
0 -1
1 0
intervalfunc:
2 ::DOIF_time($hash,0,1,$wday,$hms,"12345")
intervaltimer:
0 2
1 2
localtime:
0 1690956000
1 1691008200
2 1691004600
perlblock:
0 init
1 block_02
realtime:
0 08:00:00
1 22:30:00
2 21:30:00
time:
0 08:00:00
1 22:30:00
2 +:30
timeCond:
0 1
1 1
2 1
timer:
0 0
1 0
2 0
timers:
1 0 1 2
triggertime:
1691004600:
localtime 1691004600
hash:
1691008200:
localtime 1691008200
hash:
uiState:
uiTable:
attributesfirst 1
dev DOIF_Stocks
header
<table uitabid='DOIF-DOIF_Stocks' class=' block wide uiTabledoif doif-DOIF_Stocks ' style='border-top:none;'>
package package ui_Table;
reading totalValue
shownostate 1
table:
0:
die Tabelle blende ich mal wieder aus
var:
api YahooJSON1
stocks 0P0001HOTV.F:210:1000
blockingcalls:
Attributes:
api YahooJSON1
disable 0
event-on-change-reading .*
room Logging
stocks 0P0001HOTV.F:210:1000
uiTable {
package ui_Table;
Entscheidend sind meiner Ansicht nach
api YahooJSON1
stocks 0P0001HOTV.F:210:1000
und die werden aber nicht angezeigt, obwohl sie anscheinend in den readings erscheinen
Readings
0P0001HOTVF_change 0 2023-08-02 21:30:10
...
Okay die Readings kommen an, nur deine uiTable passt nicht. Hast du denn dort folgende Zeile eingefügt?
TPL_STOCK($SELF,0P0001HOTVF)
0P0001HOTVF entspricht quasi dem Teil deiner erzeugten Readings vor dem Underline. Wenn du mir den Inhalt deiner uiTable zeigen würdest (ist in deinen Angaben leider nicht zu sehen), dann könnte ich dir sofort sagen was fehlt. Vermutlich ist es aber die Zeile die ich erwähnt habe. Wenn noch die beiden anderen Aktien von Google und Microsoft dort auftauchen, dann versucht erst mal eine der beiden zu ersetzen, damit die Anzahl erst mal gleich bleibt. Wenn sich die Anzahl von 2 ändert, dann müsstest du noch 3-4 Zahlen zusätzlich anpassen, damit die Formatierung stimmt.
Wenn im Attribut stocks eine neue Aktie hinzugefügt wird oder eine bestehende entfernt wird, dann muss man immer die uiTable entsprechend anpassen. Den Code vom DOIF muss man hingegen nicht anpassen.
Danke, das war's - ich wusste nicht, dass ich das uiTable selbst einstellen muss. Läuft!
Hallo,
ich bekomme immer folgende Meldung:
#FHEMWEB notification:
doifUpdateCell('doif_Stocks','doifId','doif_Stocks_uiTable_c_3_4_0_0','
40.78%
','display:inline-table;')
SyntaxError: missing ) after argument list
Wo kann ich ansetzen?
vG Jens
Wann bekommst du diese Meldung? Direkt nach dem Anlegen des Device oder erst nachdem du etwas geändert hast? Welche Version hast du genau verwendet? Anscheinend klappt bei deiner uiTable etwas nicht. Kannst du mir den Inhalt der uiTable bitte posten?
Hallo,
Meldung kam immer beim Aktualisieren der Aktiendaten
Zitat... Welche Version hast du genau verwendet? ...
Gute Frage, ich vermute jetzt mal nicht aus dem ersten Post.
Habe das jetzt geändert.
Sorry und danke für den Hinweis, schönes Wochenende
Wenn es immer noch nicht geht, dann poste mir bitte noch mal alles was in uiTable drin steht, da ist dann vermutlich etwas falsch.
Yahoo API wurde mal wieder geändert und musste angepasst werden.
Leider haben sowohl S-Invester als auch Yahoo etwas geändert. Für S-Investor habe ich den Code angepasst, so das er wieder funktioniert. Für Yahoo ist das aktuell leider nicht möglich, da Yahoo jetzt mit Cookies arbeitet und ich es bisher nicht hinbekommen habe, diese irgendwie zu speichern und zu verwenden. Hier bleibe ich aber am Ball.
Ich habe allerdings ein alternatives Symbol beim stocks Attribut hinzugefügt, so das man jetzt z.b. <Yahoo Symbol>:<ISIN>:... eingeben kann. Damit wird es möglich, zwischen den APIs einfach umzuschalten, also zwischen Yahoo und S-Investor zu wechseln. Die Readings bleiben dabei gleich, so das keine weiteren Anpassungen an Charts oder der uiTable notwendig sind.
Zitat von: mumpitzstuff am 13 April 2024, 22:27:54so das man jetzt z.b. <Yahoo Symbol>:<ISIN>:..
Hm, in 95_Shares.pm habe ich das mit
<Yahoo Symbol>|<ISIN>
vor dem ersten Doppelpunkt gemacht.
Begründung: Damit kann ich dort (muss ich nicht) eine ganze ('|'-getrennte) Liste von Bezeichnungen hinterlegen, die sich jeweils auf eine andere defaultSource beziehen. Mit einem einzigen Attributwert kann ich dann zwischen verschiedenen Default-Quellen umschalten.
Ich schlage vor, dass wir uns auf einen gemeinsames Trennzeichen einigen, damit die Werte im "stocks"-Attribut kompatibel bleiben. Alles, nur nicht ':'.
LG
pah
Okay ich kann auch das | verwenden. Ich stelle das um. Ändern sich nach einer Umstellung die Readings bei dir? Bei mir habe ich das so gemacht, das immer der erste Eintrag für den Readingnamen verwendet wird, damit alle darauf aufbauenden Anzeigen nicht umgestellt werden müssen.
Btw ich habe gestern noch raus gefunden weshalb S-Investor bei dir noch geht. Ich hatte in dem DOIF noch einen Zwischenstand drin und nicht die finale Version, die dann in Quote::Finance eingeflossen ist. Danke für den Hinweis.
Zitat von: mumpitzstuff am 14 April 2024, 12:18:37erste Eintrag für den Readingnamen
Klar ändert sich das im Moment, aber nur wenig - weil ich die Sortierung nach der Langbezeichnung vornehme, und die je nach Kursanbieter unterschiedlich sein kann. Ist aber weder so absurd wie die nach Yahoo-Symbolen oder der ISIN...
LG
pah
S-Investor Code wurde noch mal angepasst und Yahoo geht wieder. Das alternative Symbol wurde gemäß des Vorschlags von pah angepasst, so das als Trennzeichen nun | verwendet werden muss. Der zusätzliche Doppelpunkt entfällt damit wieder.