Hallo zusammen,
in Anlehnung an das calendar-Plugin folgender Code
jnk.js
jnk.html
beispielhaft eingebunden in index.html
und gefüttert von einem sh.py-Item in dem als String die entsprechenden Daten stehen:
calendar.py
es braucht allerdings auch ein geändertes iCal-Plugin:
Ich werde versuchen, das alles upstream zu bringen.
Gruss,
der Jan.
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 + ' </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');
}
});
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 %}
Code:
{% import "jnk.html" as jnk %}
{{ jnk.calendar('calendarlist1', 'Kalendar', 7, gad='zentral.calendar') }}
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)
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)
Gruss,
der Jan.


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