Ankündigung

Einklappen
Keine Ankündigung bisher.

Kalendar-Widget über sh.py

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    [Codebeispiel] Kalendar-Widget über sh.py

    Hallo zusammen,

    in Anlehnung an das calendar-Plugin folgender Code

    jnk.js

    Code:
    /**
     * -----------------------------------------------------------------------------
     * @package     smartVISU
     * @author      Jan N. Klug
     * @copyright   2013
     * @license     GPL <http://www.gnu.de>
     * ----------------------------------------------------------------------------- 
     */ 
    
    $(document).delegate('div[data-widget="jnk.calendar"]', {
        'update' : function (event, response) {
            var uid = this.id;
            var ret;
            var line = '';
            var max = $(this).attr('data-max');
            data = $.parseJSON(response);
            for (var i in data) {
                if (max>0) {
                    ret = '<img style="background: ' + data[i].color + ';" class="icon" src="' + data[i].icon + '" />';
                    ret += '<div class="color" style="background: ' + $(this).attr('data-color') +';"></div>';
                    ret += '<h3>' + data[i].summary + '</h3>';
                    ret += '<p>' + data[i].start + ' bis ' + data[i].end + '&nbsp;</p>';
    
                    if (data[i].location) {
                        ret += '<span class="ui-li-count">' + data[i].location + '</span>';
                    }
    
                    ret = '<a href="' + (data[i].link ? data[i].link : '#') + '">' + ret + '</a>';
                    line += '<li data-icon="false">' + ret + '</li>';
                    max --;
                } else {
                    break;
                }
            }
            $('#' + uid + ' ul').html(line).trigger('prepare').listview('refresh').trigger('redraw');
        }
    });
    jnk.html

    Code:
    /**
     * -----------------------------------------------------------------------------
     * @package     smartVISU
     * @author      Jan N. Klug
     * @copyright   2013
     * @license     GPL <http://www.gnu.de>
     * ----------------------------------------------------------------------------- 
     */ 
     
    {% macro calendar(id, title, count, color, gad) %}
        {% set uid = uid(page, id) %}
    
        <div id="{{ uid }}" data-widget="jnk.calendar" data-item="{{gad}}" data-max="{{count}}" data-color="{{color|default('#666666')}}" class="calendarlist">
            {% if title %} <h2>{{ title }}</h2> {% endif %}
            <ul data-role="listview">
            </ul>
        </div>
    
    {% endmacro %}
    beispielhaft eingebunden in index.html

    Code:
            {% import "jnk.html" as jnk %}    
            {{ jnk.calendar('calendarlist1', 'Kalendar', 7, gad='zentral.calendar') }}
    und gefüttert von einem sh.py-Item in dem als String die entsprechenden Daten stehen:

    calendar.py
    Code:
    #!/usr/bin/env python
    # 
    
    import json
    
    dayrep = {'Monday' : 'Montag', 'Tuesday' : 'Dienstag', 'Wednesday':'Mittwoch',
       'Thursday' : 'Donnerstag', 'Friday' : 'Freitag', 'Saturday' : 'Samstag', 'Sunday':'Sonntag'}
    
    events = sh.ical(<URL>', 28)
    
    #repack events
    
    event2 = []
    for day in events:
        for event in events[day]:
            start = datetime.datetime.combine(day, event[0]).replace(tzinfo = dateutil.tz.gettz('UTC')).astimezone(sh.now().tzinfo)
            end = event[2].replace(tzinfo = dateutil.tz.gettz('UTC')).astimezone(sh.now().tzinfo)
            event2.append({ "timestamp" : time.mktime(start.timetuple()), "start": start.strftime("%A, %d.%m.%y %H:%M"), "summary" : event[1], 
                "end" : end.strftime("%H:%M") if (end.date() == start.date()) else end.strftime("%A, %d.%m.%y %H:%M")})
    
    # replace wrong locale
                
    event2.sort(key = lambda e : e["timestamp"])
    jsonstr = json.dumps(event2)
    for i, j in dayrep.iteritems():
        jsonstr = jsonstr.replace(i, j)
      
    sh.zentral.calendar(jsonstr)
    es braucht allerdings auch ein geändertes iCal-Plugin:

    Code:
    #!/usr/bin/env python
    # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
    #########################################################################
    # Copyright 2013 KNX-User-Forum e.V.            https://knx-user-forum.de/
    #########################################################################
    #  This file is part of SmartHome.py.   http://smarthome.sourceforge.net/
    #
    #  SmartHome.py is free software: you can redistribute it and/or modify
    #  it under the terms of the GNU General Public License as published by
    #  the Free Software Foundation, either version 3 of the License, or
    #  (at your option) any later version.
    #
    #  SmartHome.py is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with SmartHome.py. If not, see <http://www.gnu.org/licenses/>.
    #########################################################################
    
    import logging
    import datetime
    
    import dateutil.tz
    import dateutil.rrule
    import dateutil.relativedelta
    
    logger = logging.getLogger('')
    
    
    class iCal():
        DAYS = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")
        FREQ = ("YEARLY", "MONTHLY", "WEEKLY", "DAILY", "HOURLY", "MINUTELY", "SECONDLY")
    
        def __init__(self, smarthome):
            self._sh = smarthome
    
        def run(self):
            self.alive = True
    
        def stop(self):
            self.alive = False
    
        def parse_item(self, item):
            pass
    
        def parse_logic(self, logic):
            pass
    
        def update_item(self, item, caller=None, source=None, dest=None):
            pass
    
        def __call__(self, ics, delta=1, offset=0):
            if ics.startswith('http'):
                ical = self._sh.tools.fetch_url(ics)
                if ical is False:
                    return {}
            else:
                try:
                    with open(ics, 'r') as f:
                        ical = f.read()
                except IOError, e:
                    logger.error('Could not open ics file {0}: {1}'.format(ics, e))
                    return {}
            now = self._sh.now()
            offset = offset - 1  # start at 23:59:59 the day before
            delta += 1  # extend delta for negetiv offset
            start = now.replace(hour=23, minute=59, second=59, microsecond=0) + datetime.timedelta(days=offset)
            end = start + datetime.timedelta(days=delta)
            events = self._parse_ical(ical, ics)
            ret = {}
            for event in events:
                event = events[event]
                if 'RRULE' in event:
                    for dt in event['RRULE'].between(start, end, True):
                        if dt not in event['EXDATES']:
                            time = dt.time()
                            date = dt.date()
                            if date not in ret:
                                ret[date] = [[time, event['SUMMARY'], dt]]
                            else:
                                ret[date].append([time, event['SUMMARY'], dt])
                else:
                    dt = event['DTSTART']
                    de = event['DTEND']
                    if (dt > start and dt < end) or (dt < start and de > start):
                        time = dt.time()
                        date = dt.date()
                        if date not in ret:
                            ret[date] = [[time, event['SUMMARY'], de]]
                        else:
                            ret[date].append([time, event['SUMMARY'], de])
            return ret
    
        def _parse_date(self, val, dtzinfo, par=''):
            if par.startswith('TZID='):
                tmp, par, timezone = par.partition('=')
            if 'T' in val:  # ISO datetime
                val, sep, off = val.partition('Z')
                dt = datetime.datetime.strptime(val, "%Y%m%dT%H%M%S")
            else:  # date
                y = int(val[0:4])
                m = int(val[4:6])
                d = int(val[6:8])
                dt = datetime.datetime(y, m, d)
            dt = dt.replace(tzinfo=dtzinfo)
            return dt
    
        def _parse_ical(self, ical, ics):
            events = {}
            tzinfo = self._sh.tzinfo()
            for line in ical.splitlines():
                if line == 'BEGIN:VEVENT':
                    event = {'EXDATES': []}
                elif line == 'END:VEVENT':
                    if 'UID' not in event:
                        logger.warning("iCal: problem parsing {0} no UID for event: {1}".format(ics, event))
                        continue
                    if 'SUMMARY' not in event:
                        logger.warning("iCal: problem parsing {0} no SUMMARY for UID: {1}".format(ics, event['UID']))
                        continue
                    if 'DTSTART' not in event:
                        logger.warning("iCal: problem parsing {0} no DTSTART for UID: {1}".format(ics, event['UID']))
                        continue
                    if 'DTEND' not in event:
                        logger.warning("iCal: problem parsing {0} no DTEND for UID: {1}".format(ics, event['UID']))
                        continue
                    if 'RRULE' in event:
                        event['RRULE'] = self._parse_rrule(event, tzinfo)
                    if event['UID'] in events:
                        if 'RECURRENCE-ID' in event:
                            events[event['UID']]['EXDATES'].append(event['RECURRENCE-ID'])
                            events[event['UID'] + event['DTSTART'].isoformat()] = event
                        else:
                            logger.warning("iCal: problem parsing {0} duplicate UID: {1}".format(ics, event['UID']))
                            continue
                    else:
                        events[event['UID']] = event
                    del(event)
                elif 'event' in locals():
                    key, sep, val = line.partition(':')
                    key, sep, par = key.partition(';')
                    key = key.upper()
                    if key == 'TZID':
                        tzinfo = dateutil.tz.gettz(val)
                    elif key in ['UID', 'SUMMARY', 'SEQUENCE', 'RRULE']:
                        event[key] = val # noqa
                    elif key in ['DTSTART', 'DTEND', 'EXDATE', 'RECURRENCE-ID']:
                        try:
                            date = self._parse_date(val, tzinfo, par)
                        except Exception, e:
                            logger.warning("Problem parsing: {0}: {1}".format(ics, e))
                            continue
                        if key == 'EXDATE':
                            event['EXDATES'].append(date) # noqa
                        else:
                            event[key] = date # noqa
            return events
    
        def _parse_rrule(self, event, tzinfo):
            rrule = dict(a.split('=') for a in event['RRULE'].upper().split(';'))
            args = {}
            if 'FREQ' not in rrule:
                return
            freq = self.FREQ.index(rrule['FREQ'])
            del(rrule['FREQ'])
            if 'DTSTART' not in rrule:
                rrule['DTSTART'] = event['DTSTART']
            if 'WKST' in rrule:
                if rrule['WKST'] in self.DAYS:
                    rrule['WKST'] = self.DAYS.index(rrule['WKST'])
                else:
                    rrule['WKST'] = int(rrule['WKST'])
            if 'BYDAY' in rrule:
                day = rrule['BYDAY']
                if day.isalpha():
                    if day in self.DAYS:
                        day = self.DAYS.index(day)
                else:
                    n = int(day[0:-2])
                    day = self.DAYS.index(day[-2:])
                    day = dateutil.rrule.weekday(day, n)
                rrule['BYWEEKDAY'] = day
                del(rrule['BYDAY'])
            if 'COUNT' in rrule:
                rrule['COUNT'] = int(rrule['COUNT'])
            if 'INTERVAL' in rrule:
                rrule['INTERVAL'] = int(rrule['INTERVAL'])
            if 'UNTIL' in rrule:
                try:
                    rrule['UNTIL'] = self._parse_date(rrule['UNTIL'], tzinfo)
                except Exception, e:
                    logger.warning("Problem parsing UNTIL: {1} --- {0} ".format(event, e))
                    return
            for par in rrule:
                args[par.lower()] = rrule[par]
            return dateutil.rrule.rrule(freq, **args)
    Ich werde versuchen, das alles upstream zu bringen.

    Gruss,

    der Jan.
    KNX, DMX over E1.31, DALI, 1W, OpenHAB, MQTT

    #2
    Hallo Jan,

    schön. Du verwendest eine Liste für die Darstellung?
    Vllt. können wir ja Martin von der Notwendigkeit einer generischen Listen-Darstellung überzeugen. Oder geht Dein Widget über eine 'normale' Liste hinaus?
    SH.py unterstützt auch Listen als Datenformat das man dann für zentral.calendar verwenden könnte.

    Bis bald

    Marcus

    Kommentar


      #3
      Naja, vom Python Standpunkt ist es eine "list of dictionaries", in der aber nur die Rohdaten sind. Um das in einer generischen Liste darzustellen, müsste man die Formatierung da mit reinpacken und dann quasi den content des Listeneintrags als HTML ausliefern. Irgendwie auch nicht so richtig smart.

      Die Formatierung in der SV zu verallgemeinern ist m.E. schwierig, denn woher soll die SV wissen, in welcher Reihenfolge und wie sie die einzelnen Einträge des dictionaries darstellen soll?

      Was man sparen könnte ist das hin- und zurückwandeln über JSON. Sähe eleganter aus, bringt aber nur begrenzt was, weil es eben nur eine Zeile Python und eine Zeile JavaScript ist.

      Gruss,

      der Jan
      KNX, DMX over E1.31, DALI, 1W, OpenHAB, MQTT

      Kommentar


        #4
        Das wäre relativ leicht möglich. Das erste Value des Dictionaries (oder das mit key 'title' oder 'name') wird per Default angezeigt, alle anderen nicht. Wenn es kein Dictionary sondern nur eine Liste ist wird der Wert direkt angezeigt. Des Weiteren hat das Listenwidget einen transFN Parameter. Über diesen kannst du dem Widget dann eine beliebige Funktion mitgeben, die die Werte formatiert und das entsprechende HTML Innengerüst der Liste erzeugt (ähnlich wie ich das für speziell formatierte Values bereits gemacht habe).

        Damit hätte man eine Liste, die per Default einfach einen Wert anzeigt aber jederzeit so erweitert werden kann, dass man beliebig komplexe Inhalte in der Liste anzeigen kann.

        Weiter kann man noch ein zusätzlich notwendiges Item im Dictionary z.B. 'val' definieren, das in einer erweiterten Version des List Widgets als Wert zu Setzen beim Anklicken eines bestimmten Values an eine definierte GA/Item geschickt wird.
        Mit freundlichen Grüßen
        Niko Will

        Logiken und Schnittstelle zu anderen Systemen: smarthome.py - Visualisierung: smartVISU
        - Gira TS3 - iPhone & iPad - Mobotix T24 - ekey - Denon 2313 - Russound C5 (RIO over TCP Plugin) -

        Kommentar


          #5
          Hab das mal schnell zusammen gezimmert (ungetestet). Sollte aber so in etwa funktionieren:

          Code:
          $(document).delegate('div[data-widget="basic.list"]', {
              'update' : function (event, response) {
                  var data = $.parseJSON(response);
                  var format = $(this).attr('data-format');
                  var result = '';
          
                  for (var i in data) {
                      if (format) {
                          result += window[format](data[i]);
                      } else {
                          result += '<li data-icon="false" data-value="' + data[i].value + '">' + data[i].title + '</li>';
                      }
                  }
          
                  $('#' + uid + ' ul').html(result).trigger('prepare').listview('refresh').trigger('redraw');
              }
          });
          Code:
          var formatCalendar = function(data) {
              var ret = '<img style="background: ' + data.color + ';" class="icon" src="' + data.icon + '" />';
              ret += '<div class="color" style="background: ' + $(this).attr('data-color') +';"></div>';
              ret += '<h3>' + data.summary + '</h3>';
              ret += '<p>' + data.start + ' bis ' + data.end + '&nbsp;</p>';
          
              if (data.location) {
                  ret += '<span class="ui-li-count">' + data.location + '</span>';
              }
          
              ret = '<a href="' + (data.link ? data.link : '#') + '">' + ret + '</a>';
              return '<li data-icon="false">' + ret + '</li>';
          };
          Code:
          {% macro calendar(id, title, count, color, gad) %}
              {% set uid = uid(page, id) %}
          
              <div id="{{ uid }}" data-widget="basic.list" data-item="{{gad}}" data-format="formatCalendar" class="calendarlist">
                  <ul data-role="listview">
                  </ul>
              </div>
          
          {% endmacro %}
          Mit freundlichen Grüßen
          Niko Will

          Logiken und Schnittstelle zu anderen Systemen: smarthome.py - Visualisierung: smartVISU
          - Gira TS3 - iPhone & iPad - Mobotix T24 - ekey - Denon 2313 - Russound C5 (RIO over TCP Plugin) -

          Kommentar


            #6
            Ah danke. Mit dem Code-Beispiel hab ichs dann auch verstanden. Finde ich gut, werde ich mal testen.

            Martin? Wie siehts denn damit aus?

            Gruss,

            der Jan
            KNX, DMX over E1.31, DALI, 1W, OpenHAB, MQTT

            Kommentar


              #7
              Ja, das das Listen-Thema ganz oben auf der Liste steht ist klar . Ich bin mir noch nicht ganz sicher, wie "universell" das am Besten ist. Der Ansatz von Niko ist schon echt gut, obwohl ich eigentlich kein Freund von dynamischen Funktionsaufrufen bin. Vielleicht fällt mir da noch ne gute Variante ein.

              Gruss
              Join smartVISU on facebook. Web: smartvisu.de.
              Dir gefällt smartVISU? Bitte spenden für die Weiterentwicklung.

              Kommentar


                #8
                Kalendar-Widget über sh.py

                Du kannst auch einfach für jede Art von Liste ein eigenes Widget machen, wie bei den Plots.
                Mit freundlichen Grüßen
                Niko Will

                Logiken und Schnittstelle zu anderen Systemen: smarthome.py - Visualisierung: smartVISU
                - Gira TS3 - iPhone & iPad - Mobotix T24 - ekey - Denon 2313 - Russound C5 (RIO over TCP Plugin) -

                Kommentar


                  #9
                  Ja, ich glaub auch das das der Weg wäre wo ich hin tendieren würde.
                  Join smartVISU on facebook. Web: smartvisu.de.
                  Dir gefällt smartVISU? Bitte spenden für die Weiterentwicklung.

                  Kommentar


                    #10
                    Hallo,

                    ich wollte den Thread mal wieder hochholen und fragen, ob da schon etwas entstanden ist ? :-)

                    Ich bin nämlich am Punkt angekommen, dass ich Listen in der Visu brauche, die von sh.py betrieben werden.

                    Ansonsten muß ich das selbst angehen :-(

                    Grüße Michel

                    Kommentar


                      #11
                      Hallo, ich wollte das Thema auch nochmal ausgraben. Gibt's hier einen aktuellen Stand? Gruß Felix

                      Kommentar


                        #12
                        Ich nutze inzwischen weder sh.py noch Smartvisu, daher gibts von mir nichts neues und es wir auch nichts kommen.
                        KNX, DMX over E1.31, DALI, 1W, OpenHAB, MQTT

                        Kommentar

                        Lädt...
                        X