DOIF für Aktienkurse über Yahoo Finance und S-Investor (non blocking)(Codeschnipsel)

Begonnen von mumpitzstuff, 12 Juli 2023, 21:23:30

Vorheriges Thema - Nächstes Thema

mumpitzstuff

History:
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::CookieJar::LWP;;\
  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_symbol_alt, $stock_count, $stock_initvalue) = split(':', $stock);;\
      my $url = $url_template;;\
      \
      ## use alternative symbol if needed\
      if ((($_api =~ /YahooJSON/) && length($stock_symbol) == 12 && looks_like_number(substr($stock_symbol, -10))) ||\
          (($_api =~ /S-Investor/) && !(length($stock_symbol) == 12 && looks_like_number(substr($stock_symbol, -10)))))\
      {\
        $url =~ s/#REPLACE#/$stock_symbol_alt/;;\
      }\
      else\
      {\
        $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');;\
      $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 $cookie_jar = HTTP::CookieJar::LWP->new();;\
          $ua->cookie_jar($cookie_jar);;\
          \
          ## get cookie\
          $reply = $ua->request(GET 'https://login.yahoo.com');;\
          \
          ## get crumb\
          $reply = $ua->request(GET 'https://query2.finance.yahoo.com/v1/test/getcrumb');;\
          \
          my $crumb = '';;\
          ##if (200 == $reply->code)\
          ##{\
            $crumb = $reply->content;;\
          ##}\
          \
          $reply = $ua->request(GET $url.'&crumb='.$crumb);;\
          \
          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.'|'.$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('_tag'=>'table');;\
\
            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('_tag'=>'table'))[2];;\
\
            $td1 = ($lastvalue->look_down('_tag'=>'td'))[5];;\
            @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] eq '-') ? '' : $splitopen[0];;\
\
            $td1 = ($lastvalue->look_down('_tag'=>'td'))[8];;\
            @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] eq '-') ? '' : $splithigh[0];;\
\
            $td1 = ($lastvalue->look_down('_tag'=>'td'))[11];;\
            @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] eq '-') ? '' : $splitlow[0];;\
\
            $td1 = ($lastvalue->look_down('_tag'=>'td'))[14];;\
            @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] eq '-') ? '' : $splitchange[0];;\
\
            $td1 = ($lastvalue->look_down('_tag'=>'td'))[17];;\
            @child = $td1->content_list;;\
            my $changep = $child[0];;\
            $changep =~ s/[\.|%]//g;;\
            $changep =~ s/,/\./;;\
            $changep = '' if ($changep eq '-');;\
\
            $td1 = ($lastvalue->look_down('_tag'=>'td'))[35];;\
            @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 _\
            $shortname =~ s/\|/_/g;;\
            $longname =~ s/(\||&amp;;)/_/g;;\
\
            $stock_count = 1 if (!defined($stock_count));;\
            $stock_initvalue = 0 if (!defined($stock_initvalue));;\
\
            $output .= '|'.$stock_symbol.'|'.$symbol.'|'.$shortname.'|'.$longname.'|'.$type.'|'.$currency.'|'.\
                           $exchangeSymbol.'|'.$exchangeName.'|'.$marketState.'|'.$timestamp.'|'.\
                           $volume.'|'.$open.'|'.$high.'|'.$low.'|'.$close.'|'.$change.'|'.\
                           $changep.'|'.$price.'|'.$delay.'|'.$stock_count.'|'.$stock_initvalue;;\
\
            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.'_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_symbol_alt, $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));;\
      }\
      \
      $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 S-InvestorXETRA
attr doif_Stocks event-on-change-reading .*
attr doif_Stocks room FINANCE
attr doif_Stocks stocks US02079K3059:GOOG: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{$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)\
"&nbsp;;"|"&nbsp;;"|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]))|"&nbsp;;"|"&nbsp;;"\


Das Attribut stocks setzt sich wie folgt zusammen (mehrere Angaben müssen mit einem Komma getrennt werden):

<symbol>:<alternatives symbol oder leer>:<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.

Damian

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

betateilchen

-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

mumpitzstuff

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.

mumpitzstuff

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.

mumpitzstuff


Damian

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

mumpitzstuff

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).

carlos

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
FHEM svn auf Intel NUC mit proxmox,1 UDOO, 3 Raspberry Pi, signalduino, nanoCUL, div. Homematic Komponenten, toom Baumarkt Funksteckdosen, einige sonoffs, hue, shelly

mumpitzstuff

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.

Per

Man könnte es mit Variablen versuchen, leider werden diese nicht immer genommen (siehe TV DOIF), ein System konnte ich nicht erkennen.

carlos

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
FHEM svn auf Intel NUC mit proxmox,1 UDOO, 3 Raspberry Pi, signalduino, nanoCUL, div. Homematic Komponenten, toom Baumarkt Funksteckdosen, einige sonoffs, hue, shelly

mumpitzstuff

Vielen Dank! Ich schaue es mir an und nehme das mit auf, falls es auch bei mir funktioniert.

mumpitzstuff

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...

Per


mumpitzstuff

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/(\||&amp;;)/_/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)\
"&nbsp;;"|"&nbsp;;"|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]))|"&nbsp;;"|"&nbsp;;"\

Damian

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($)
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

mumpitzstuff

Stimmt. Hab ich ja teilweise auch schon so. Ist irgendwie durchgerutscht. Behebe ich beim nächsten Update. Danke!

mumpitzstuff

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.

andies

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?
FHEM 6.1 auf RaspPi3 (Raspbian:  6.1.21-v8+; Perl: v5.32.1)
SIGNALduino (433 MHz) und HM-UART (868 MHz), Sonoff, Blitzwolf, Somfy RTS, CAME-Gartentor, Volkszähler, Keyence-Sensor, Homematic-Sensoren und -thermostat, Ferraris-Zähler für Wasseruhr, Openlink-Nachbau Viessmann

mumpitzstuff

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.

andies

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
...
FHEM 6.1 auf RaspPi3 (Raspbian:  6.1.21-v8+; Perl: v5.32.1)
SIGNALduino (433 MHz) und HM-UART (868 MHz), Sonoff, Blitzwolf, Somfy RTS, CAME-Gartentor, Volkszähler, Keyence-Sensor, Homematic-Sensoren und -thermostat, Ferraris-Zähler für Wasseruhr, Openlink-Nachbau Viessmann

mumpitzstuff

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.

andies

Danke, das war's - ich wusste nicht, dass ich das uiTable selbst einstellen muss. Läuft!
FHEM 6.1 auf RaspPi3 (Raspbian:  6.1.21-v8+; Perl: v5.32.1)
SIGNALduino (433 MHz) und HM-UART (868 MHz), Sonoff, Blitzwolf, Somfy RTS, CAME-Gartentor, Volkszähler, Keyence-Sensor, Homematic-Sensoren und -thermostat, Ferraris-Zähler für Wasseruhr, Openlink-Nachbau Viessmann

Newbie

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
fhem-6.1 (configDB+DbLog)  auf ODROID-XU4

mumpitzstuff

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?

Newbie

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
fhem-6.1 (configDB+DbLog)  auf ODROID-XU4

mumpitzstuff

Wenn es immer noch nicht geht, dann poste mir bitte noch mal alles was in uiTable drin steht, da ist dann vermutlich etwas falsch.

mumpitzstuff


mumpitzstuff

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.

Prof. Dr. Peter Henning

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

mumpitzstuff

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.

Prof. Dr. Peter Henning

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