def __init__(self, sh, *args, **kwargs): from bin.smarthome import VERSION if '.'.join(VERSION.split('.', 2)[:2]) <= '1.5': self.logger = logging.getLogger(__name__) self._apiKey = self.get_parameter_value('apiKey') self._userKey = self.get_parameter_value('userKey') self._device = self.get_parameter_value('device') self._po = Http()
def __init__(self, smarthome): super().__init__() if '.'.join(VERSION.split('.', 2)[:2]) <= '1.5': self.logger = logging.getLogger(__name__) try: self.shtime = Shtime.get_instance() self.handle_login = self.get_parameter_value('handle_login') try: self.dl = Http(timeout=self.get_parameter_value('timeout'), hide_login=self.handle_login) except: self.dl = Http(hide_login=self.handle_login) self._items = [] self._icals = {} self._ical_aliases = {} self._cycle = self.get_parameter_value('cycle') calendars = self.get_parameter_value('calendars') config_dir = self.get_parameter_value('directory') except Exception as err: self.logger.error('Problems initializing: {}'.format(err)) self._init_complete = False return try: self._directory = '{}/{}'.format(self.get_vardir(), config_dir) except Exception: self._directory = '{}/var/{}'.format(smarthome.get_basedir(), config_dir) self._directory = os.path.normpath(self._directory) try: os.makedirs(self._directory) self.logger.debug('Created {} subfolder in var'.format(config_dir)) except OSError as e: if e.errno != errno.EEXIST: self.logger.error( 'Problem creating {} folder in {}/var'.format( config_dir, smarthome.get_basedir())) self._init_complete = False return for calendar in calendars: if isinstance(calendar, dict): calendar = list("{!s}:{!s}".format(k, v) for (k, v) in calendar.items())[0] if ':' in calendar and 'http' != calendar[:4]: name, _, cal = calendar.partition(':') calendar = cal.strip() self.logger.info( 'Registering calendar {} with alias {}.'.format( Network.clean_uri(calendar, self.handle_login), name)) self._ical_aliases[name.strip()] = calendar else: self.logger.info( 'Registering calendar {} without alias.'.format( Network.clean_uri(calendar, self.handle_login))) calendar = calendar.strip() self._icals[calendar] = self._read_events(calendar) self.shtime = Shtime.get_instance()
def __init__(self, sh): """ Initalizes the plugin. """ self.logger.debug("Init method called") # Call init code of parent class (SmartPlugin) super().__init__() from bin.smarthome import VERSION if '.'.join(VERSION.split('.', 2)[:2]) <= '1.5': self.logger = logging.getLogger(__name__) self._items = [] # get the parameters for the plugin (as defined in metadata plugin.yaml): self._account = self.get_parameter_value('account') self._password = EcoVacsAPI.md5(self.get_parameter_value('password')) self._wanted_device = self.get_parameter_value('device') self.mybot = { 'nick': None, 'did': None, 'country': self.get_parameter_value('country').lower(), 'continent': self.get_parameter_value('continent').lower(), 'model': None, 'iconURL': None, 'live_map': None, 'last_clean_logs': [], 'last_clean_map': None, 'available': False, 'battery_level': 0, 'state': None, 'state_text': None, 'fan_speed': None, 'water_level': None, 'components': [], 'rooms': [] } # Check if country and continent defined, if not try to autolocate http = Http() if not self.mybot['country'] or not self.mybot['continent']: _locate = http.get_json( 'http://ip-api.com/json?fields=continentCode,countryCode') if not self.mybot['country']: if _locate and _locate['countryCode']: self.logger.info('Autodetected country: {}'.format( _locate['countryCode'])) self._update_items('country', _locate['countryCode'].lower()) else: self.logger.error( 'No country defined and autolocate not possible, please specify country in plugin configuration !' ) self._init_complete = False return if not self.mybot['continent']: if _locate and _locate['continentCode']: self.logger.info('Autodetected continent: {}'.format( _locate['continentCode'])) self._update_items('continent', _locate['continentCode'].lower()) else: self.logger.error( 'No continent defined and autolocate not possible, please specify continent in plugin configuration !' ) self._init_complete = False return self.device = None self._device_id = "".join( random.choice(string.ascii_uppercase + string.digits) for _ in range(16)) self._cycle = self.get_parameter_value('interval') self._items = {} if not self.init_webinterface(): self._init_complete = False return
class Pushover(SmartPlugin): PLUGIN_VERSION = "1.6.1.0" _url = "https://api.pushover.net/1/messages.json" def __init__(self, sh, *args, **kwargs): from bin.smarthome import VERSION if '.'.join(VERSION.split('.', 2)[:2]) <= '1.5': self.logger = logging.getLogger(__name__) self._apiKey = self.get_parameter_value('apiKey') self._userKey = self.get_parameter_value('userKey') self._device = self.get_parameter_value('device') self._po = Http() def run(self): self.alive = True def stop(self): self.alive = False def __call__(self, title=None, message='', priority=None, retry=None, expire=None, sound=None, url=None, url_title=None, device=None, userKey=None, apiKey=None, attachment=None): data = {} data['timestamp'] = int(time.time()) if title: data['title'] = title[:250] data['message'] = message[:1000] if priority: if isinstance(priority, int) and priority >= -2 and priority <= 2: data['priority'] = priority if retry and priority == 2: if isinstance(retry, int) and retry >= 30: data['retry'] = retry else: data['retry'] = 30 self.logger.error( "Pushover message retry need at least 30 secounds! I set it to 30!" ) elif not retry and priority == 2: self.logger.error( "Pushover message priority = 2 need retry to be set, degrade priority to 1!" ) data['priority'] = 1 if expire and priority == 2: if isinstance(expire, int) and expire <= 10800: data['expire'] = expire else: data['expire'] = 10800 self.logger.error( "Pushover message expire need at most 10800 secounds! I set it to 10800." ) elif not expire and priority == 2: self.logger.error( "Pushover message priority = 2 need expire to be set, degrade priority to 1!" ) data['priority'] = 1 # delete not used vars if data['priority'] < 2: if 'expire' in data: del data['expire'] if 'retry' in data: del data['retry'] else: self.logger.error( "Pushover message priority need to be a number between -2 and 2!" ) if sound: data['sound'] = sound if url: data['url'] = url[:512] if url_title: data['url_title'] = url_title[:100] if userKey: data['user'] = userKey elif self._userKey: data['user'] = self._userKey else: self.logger.error("Pushover needs a userKey") return if apiKey: data['token'] = apiKey elif self._apiKey: data['token'] = self._apiKey else: self.logger.error("Pushover needs a apiKey") return if device: data['device'] = device elif self._device: data['device'] = self._device if attachment: files = {'attachment': open(attachment, 'rb')} else: files = {} try: json_data = self._po.post_json(self._url, json=data, files=files) (_status_code, _reason) = self._po.response_status() # process response if (_status_code == 200 and json_data['status'] == 1): self.logger.debug( "Pushover returns: Notification successful submitted.") elif (_status_code == 429): self.logger.warning( "Pushover returns: Message limits have been reached.") else: self.logger.warning("Pushover returns: {0} - {1}".format( _status_code, _reason)) except Exception as e: self.logger.warning( "Could not send Pushover notification. Error: {}".format(e))
class iCal(SmartPlugin): PLUGIN_VERSION = "1.5.4" ALLOW_MULTIINSTANCE = False DAYS = ("MO", "TU", "WE", "TH", "FR", "SA", "SU") FREQ = ("YEARLY", "MONTHLY", "WEEKLY", "DAILY", "HOURLY", "MINUTELY", "SECONDLY") PROPERTIES = ("SUMMARY", "DESCRIPTION", "LOCATION", "CATEGORIES", "CLASS") def __init__(self, smarthome): super().__init__() if '.'.join(VERSION.split('.', 2)[:2]) <= '1.5': self.logger = logging.getLogger(__name__) try: self.shtime = Shtime.get_instance() try: self.dl = Http(timeout=self.get_parameter_value('timeout')) except: self.dl = Http('') self._items = [] self._icals = {} self._ical_aliases = {} cycle = self.get_parameter_value('cycle') calendars = self.get_parameter_value('calendars') config_dir = self.get_parameter_value('directory') self.handle_login = self.get_parameter_value('handle_login') except Exception as err: self.logger.error('Problems initializing: {}'.format(err)) self._init_complete = False return try: self._directory = '{}/{}'.format(self.get_vardir(), config_dir) except Exception: self._directory = '{}/var/{}'.format(smarthome.get_basedir(), config_dir) self._directory = os.path.normpath(self._directory) try: os.makedirs(self._directory) self.logger.debug('Created {} subfolder in var'.format(config_dir)) except OSError as e: if e.errno != errno.EEXIST: self.logger.error( 'Problem creating {} folder in {}/var'.format( config_dir, smarthome.get_basedir())) self._init_complete = False return for calendar in calendars: if isinstance(calendar, dict): calendar = list("{!s}:{!s}".format(k, v) for (k, v) in calendar.items())[0] if ':' in calendar and 'http' != calendar[:4]: name, _, cal = calendar.partition(':') calendar = cal.strip() self.logger.info( 'Registering calendar {} with alias {}.'.format( self._clean_uri(calendar), name)) self._ical_aliases[name.strip()] = calendar else: self.logger.info( 'Registering calendar {} without alias.'.format( self._clean_uri(calendar))) calendar = calendar.strip() self._icals[calendar] = self._read_events(calendar) self.shtime = Shtime.get_instance() self.scheduler_add('iCalUpdate', self._update_items, cron='* * * *', prio=5) self.scheduler_add('iCalRefresh', self._update_calendars, cycle=int(cycle), prio=5) def run(self): self.alive = True def stop(self): self.scheduler_remove('iCalUpdate') self.scheduler_remove('iCalRefresh') self.alive = False def parse_item(self, item): if self.has_iattr(item.conf, 'ical_calendar'): uri = self.get_iattr_value(item.conf, 'ical_calendar').strip() if uri in self._ical_aliases: uri = self._ical_aliases[uri] if uri not in self._icals: self._icals[uri] = self._read_events(uri) self._items.append(item) 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, username=None, password=None, prio=1, verify=True): if ics in self._ical_aliases: self.logger.debug( 'iCal retrieve events by alias {0} -> {1}'.format( ics, self._ical_aliases[ics])) return self._filter_events(self._icals[self._ical_aliases[ics]], delta, offset) if ics in self._icals: self.logger.debug('iCal retrieve cached events {0}'.format(ics)) return self._filter_events(self._icals[ics], delta, offset) self.logger.debug('iCal retrieve events {0}'.format(ics)) return self._filter_events( self._read_events(ics, username=username, password=password, prio=prio, verify=verify), delta, offset) def _update_items(self): if len(self._items): now = self.shtime.now() events = {} for calendar in self._icals: events[calendar] = self._filter_events(self._icals[calendar], 0, 0) for item in self._items: calendar = item.conf['ical_calendar'] if calendar in self._ical_aliases: calendar = self._ical_aliases[calendar] val = False for date in events[calendar]: for event in events[calendar][date]: if event['Start'] <= now <= event['End'] or ( event['Start'] == event['End'] and event['Start'] <= now <= event['End'].replace( second=59, microsecond=999)): val = True break item(val) def _update_calendars(self): for uri in self._icals: self._icals[uri] = self._read_events(uri) self.logger.debug('Updated calendar {0}'.format( self._clean_uri(uri))) if len(self._icals): self._update_items() def _filter_events(self, events, delta=1, offset=0, prio=1): now = self.shtime.now() offset = offset - 1 # start at 23:59:59 the day before delta += 1 # extend delta for negative offset start = now.replace(hour=23, minute=59, second=59, microsecond=0) + datetime.timedelta(days=offset) end = start + datetime.timedelta(days=delta) revents = {} for event in events: event = events[event] e_start = event['DTSTART'] e_end = event['DTEND'] if 'RRULE' in event and (event['RRULE'] is not None): e_duration = e_end - e_start for e_rstart in event['RRULE'].between(start, end, inc=True): if e_rstart not in event['EXDATES']: date = e_rstart.date() revent = { 'Start': e_rstart, 'End': e_rstart + e_duration } for prop in self.PROPERTIES: if prop in event: revent[prop.capitalize()] = event[prop] if date not in revents: revents[date] = [revent] else: revents[date].append(revent) else: self.logger.info( "Removed {0} because it is in the excluded dates list" .format(e_rstart)) else: if (e_start > start and e_start < end) or (e_start < start and e_end > start): date = e_start.date() revent = {'Start': e_start, 'End': e_end} for prop in self.PROPERTIES: if prop in event: revent[prop.capitalize()] = event[prop] if date not in revents: revents[date] = [revent] else: revents[date].append(revent) return revents def _read_events(self, ics, username=None, password=None, prio=1, verify=True): if ics.startswith('http'): _, _, cal = ics.partition('//') name = '{}.ics'.format(cal.split('/')[0].replace('.', '_')) for entry in self._ical_aliases: name = '{}.ics'.format( entry) if ics == self._ical_aliases[entry] else name filename = '{}/{}'.format(self._directory, name) auth = 'HTTPBasicAuth' if username else None downloaded = self.dl.download(url=ics, local=filename, params={ username: username, password: password }, verify=verify, auth=auth) if downloaded is False: self.logger.error( 'Could not download online ics file {0}.'.format(ics)) return {} self.logger.debug( 'Online ics {} successfully downloaded to {}'.format( ics, filename)) ics = os.path.normpath(filename) try: ics = ics.replace('\\', os.sep).replace('/', os.sep) ics = '{}/{}'.format(self._directory, ics) if self._directory not in ics else ics with open(ics, 'r') as f: ical = f.read() self.logger.debug('Read offline ical file {}'.format(ics)) except IOError as e: self.logger.error( 'Could not open local ics file {0} (directory {1}): {2}'. format(ics, self._directory, e)) return {} return self._parse_ical(ical, ics, prio) #parse different date formats used in google calendar. Timezone is either coded in time field cointaining ('T') or in separate TZID field. def _parse_date(self, val, dtzinfo, par=''): if 'T' in val: # ISO datetime val, sep, off = val.partition('Z') dt = datetime.datetime.strptime(val, "%Y%m%dT%H%M%S") # 'Z' indicates 'Zulu', thus UTC time, therefore specify time zone utc: dt = dt.replace(tzinfo=datetime.timezone.utc) # the following condition occurs for complete day schedules. They do not have the 'T' lettre in start/end times. else: # date y = int(val[0:4]) m = int(val[4:6]) d = int(val[6:8]) dt = datetime.datetime(y, m, d) # Using timestamp configured in smarthome as reference for all complete day timestamps: dt = dt.replace(tzinfo=dtzinfo) # handling of series calendar entries: if par.startswith('TZID='): tmp, par, timezoneFromCalendar = par.partition('=') #self.logger.debug('Decoding series entry with time zone: {0}'.format(timezoneFromCalendar)) #self.logger.debug('Datetime before conversion: {0}'.format(dt)) calendar_tz = dateutil.tz.gettz(timezoneFromCalendar) dt = dt.replace(tzinfo=calendar_tz) #self.logger.debug('Datetime after conversion for series entries: {}'.format(dt)) # convert all time stamps to local timezone, configured in smarthome dt = dt.astimezone(self.shtime.tzinfo()) #self.logger.debug('Datetime after final conversion in plugin ical: {}'.format(dt)) #convert time based on time zone info which has been extracted on a higher level: #dt = dt.astimezone(dtzinfo) return dt def _parse_ical(self, ical, ics, prio): events = {} tzinfo = self.shtime.tzinfo() ical = ical.replace("\r\n\\n", ", ") ical = ical.replace("\n\\n", ", ").replace("\\n", ", ") ical = ical.replace("\\n", ", ") ical = ical.replace("\r ", "").replace( "\n ", "") # in case long lines continue in the next line for line in ical.splitlines(): if line == 'BEGIN:VEVENT': prio_count = { 'UID': 1, 'SUMMARY': 1, 'SEQUENCE': 1, 'RRULE': 1, 'CLASS': 1, 'DESCRIPTION': 1 } event = {'EXDATES': []} elif line == 'END:VEVENT': if 'UID' not in event: self.logger.warning( "problem parsing {0} no UID for event: {1}".format( ics, event)) continue if 'SUMMARY' not in event: self.logger.warning( "problem parsing {0} no SUMMARY for UID: {1}".format( ics, event['UID'])) continue if 'DTSTART' not in event: self.logger.warning( "problem parsing {0} no DTSTART for UID: {1}".format( ics, event['UID'])) continue if 'DTEND' not in event: self.logger.warning( "Warning in parsing {0} no DTEND for UID: {1}. Setting DTEND from DTSTART" .format(ics, event['UID'])) # Set end to start time: event['DTEND'] = event['DTSTART'] 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: self.logger.warning( "problem parsing {0} duplicate UID: {1} ({2})". format(ics, event['UID'], event['SUMMARY'])) 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() # why does the folowing code overwrite the time zone info configured in smarthomeNG? if key == 'TZID': tzinfo = dateutil.tz.gettz(val) self.logger.warning('Debug time zone: {0}'.format(val)) elif key in [ 'UID', 'SUMMARY', 'SEQUENCE', 'RRULE', 'CLASS', 'DESCRIPTION' ]: if event.get(key) is None or prio_count[key] == prio: prio_count[key] = prio_count.get(key) + 1 event[key] = val else: self.logger.debug( 'Value {} for entry {} ignored because of prio setting' .format(val, key)) elif key in ['DTSTART', 'DTEND', 'EXDATE', 'RECURRENCE-ID']: try: date = self._parse_date(val, tzinfo, par) except Exception as e: if key == 'EXDATE': date = [] values = val.split(",") for value in values: date.append( self._parse_date(value, tzinfo, par)) else: self.logger.warning( "Problem parsing: {0}: {1}".format(ics, e)) continue if key == 'EXDATE': event['EXDATES'].append(date) # noqa else: event[key] = date # noqa else: event[key] = val # noqa try: event['EXDATES'] = [y for x in event['EXDATES'] for y in x] except Exception: pass return events def _parse_rrule(self, event, tzinfo): rrule = dict(a.split('=') for a in event['RRULE'].upper().split(';')) self.logger.debug("Rrule {0}".format(rrule)) args = {} if 'FREQ' not in rrule: self.logger.debug("Rrule has no frequency") return freq = self.FREQ.index(rrule['FREQ']) self.logger.debug("Frequency: {0}".format(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: days = rrule['BYDAY'].split(',') new_days = [] for day in days: day = day.strip() 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) new_days.append(day) rrule['BYWEEKDAY'] = new_days del (rrule['BYDAY']) if 'COUNT' in rrule: rrule['COUNT'] = int(rrule['COUNT']) if 'INTERVAL' in rrule: rrule['INTERVAL'] = int(rrule['INTERVAL']) if 'BYMONTHDAY' in rrule: rrule['BYMONTHDAY'] = int(rrule['BYMONTHDAY']) if 'BYMONTH' in rrule: rrule['BYMONTH'] = int(rrule['BYMONTH']) if 'UNTIL' in rrule: try: rrule['UNTIL'] = self._parse_date(rrule['UNTIL'], tzinfo) except Exception as e: self.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) def _clean_uri(self, uri): # check for http[s]?://user:[email protected]/... style uris and strip name/pass pattern = re.compile('http([s]?)://([^:]+:[^@]+@)') if self.handle_login == 'show' or not pattern.match(uri): return uri replacement = {'strip': 'http\g<1>://', 'mask': 'http\g<1>://***:***@'} return pattern.sub(replacement[self.handle_login], uri)
def __init__(self, smarthome): if '.'.join(VERSION.split('.', 2)[:2]) <= '1.5': self.logger = logging.getLogger(__name__) try: self.shtime = Shtime.get_instance() try: self.dl = Http(timeout=self.get_parameter_value('timeout')) except: self.dl = Http('') self._items = [] self._icals = {} self._ical_aliases = {} self.sh = smarthome cycle = self.get_parameter_value('cycle') calendars = self.get_parameter_value('calendars') config_dir = self.get_parameter_value('directory') except Exception as err: self.logger.error('Problems initializing: {}'.format(err)) self._init_complete = False return try: self._directory = '{}/{}'.format(self.get_vardir(), config_dir) except Exception: self._directory = '{}/var/{}'.format(self.sh.get_basedir(), config_dir) try: os.makedirs(self._directory) self.logger.debug('Created {} subfolder in var'.format(config_dir)) except OSError as e: if e.errno != errno.EEXIST: self.logger.error( 'Problem creating {} folder in {}/var'.format( config_dir, self.sh.get_basedir())) self._init_complete = False return for calendar in calendars: if isinstance(calendar, dict): calendar = list("{!s}:{!s}".format(k, v) for (k, v) in calendar.items())[0] if ':' in calendar and 'http' != calendar[:4]: name, _, cal = calendar.partition(':') calendar = cal.strip() self.logger.info( 'Registering calendar {} with alias {}.'.format( calendar, name)) self._ical_aliases[name.strip()] = calendar else: self.logger.info( 'Registering calendar {} without alias.'.format(calendar)) calendar = calendar.strip() self._icals[calendar] = self._read_events(calendar) smarthome.scheduler.add('iCalUpdate', self._update_items, cron='* * * *', prio=5) smarthome.scheduler.add('iCalRefresh', self._update_calendars, cycle=int(cycle), prio=5)