def __init__(self, *args, **kw): super(NestWorkflow, self).__init__(*args, **kw) self.keychain = Keychain('jc-nest') self.account = self._get_account() self.nest = None LOG.debug('nests: %s', self.account.nests) if 'nest' in self.config: try: self.nest = self.account.nests[self.config['nest']] LOG.debug('using saved nest id %s', self.config['nest']) except Exception: LOG.exception('unable to use saved nest ID %s', self.config['nest']) if not self.nest: LOG.debug('using first nest id') self.nest = self.account.nests.values()[0] self.config['nest'] = self.nest.id
class NestWorkflow(Workflow): def __init__(self, *args, **kw): super(NestWorkflow, self).__init__(*args, **kw) self.keychain = Keychain('jc-nest') self.account = self._get_account() self.nest = None LOG.debug('nests: %s', self.account.nests) if 'nest' in self.config: try: self.nest = self.account.nests[self.config['nest']] LOG.debug('using saved nest id %s', self.config['nest']) except Exception: LOG.exception('unable to use saved nest ID %s', self.config['nest']) if not self.nest: LOG.debug('using first nest id') self.nest = self.account.nests.values()[0] self.config['nest'] = self.nest.id def _get_account(self): '''Get an authenticated Nest object''' LOG.debug('getting Nest') account = nestlib.Account(cache_dir=self.cache_dir) if not account.has_session: entry = self.keychain.get_password('nest') if not entry: self.show_message( 'First things first...', 'Before you can use this workflow you need to supply your ' "Nest account email and password. This information will " 'be stored in your login keychain.') while True: if entry: email = entry['comment'] password = entry['password'] else: btn, email = self.get_from_user( 'Email', 'Nest account email address') if btn == 'Cancel': account = None break btn, password = self.get_from_user( 'Password', 'Nest account password', hidden=True) if btn == 'Cancel': account = None break if account.login(email, password): if not entry: self.show_message('Success!', "You're logged in an ready to go!") self.keychain.set_password('nest', password, comment=email) break else: entry = None self.show_message('Error logging in', 'Either the Nest service is temporarily ' 'down or your email and password were ' 'incorrect. Click OK to re-enter your ' 'login information.') return account def do_debug(self, ignored): '''Opent the debug log file''' LOG.debug('opening log file %s', self.log_file) import os os.system('open "{0}"'.format(self.log_file)) def tell_nest(self, query): '''Display the available Nests''' LOG.debug('listing Nests') items = [] for nest in self.account.nests.values(): title = unicode(nest.name) if nest.id == self.nest.id: title += u' (active)' subtitle = u'ID: {0} Location: {1}'.format(nest.id, nest.structure.name) items.append(Item(title, subtitle=subtitle, arg=nest.id, valid=True)) if len(query.strip()) > 0: q = query.strip().lower() items = self.fuzzy_match_list(q, items, key=lambda i: i.title.lower()) return items def do_nest(self, nest_id): '''Select the active Nest''' LOG.debug('selecting Nest') self.nest = self.account.nests[nest_id] self.config['nest'] = nest_id self.puts(u'Set active Nest to "{0}" ({1})'.format(self.nest.name, nest_id)) def do_clear(self, ignored): '''Clear your nest session''' self.nest.account.clear_session() self.puts(u'Session cleared') def tell_target(self, temp): '''Tell the target temperature''' LOG.debug('telling target temperature') target = self.nest.target_temperature temp = temp.strip() temp = temp.strip('"') temp = temp.strip("'") units = self.nest.scale.upper() if temp: if self.nest.mode == 'range': temps = temp.split() if len(temps) != 2: return [Item('Waiting for valid input...')] for t in temps: if len(t) == 1 or len(t) > 2: return [Item('Waiting for valid input...')] return [Item(u'Set temperature range to %.1f°%s - %.1f°%s' % ( float(temps[0]), units, float(temps[1]), units), arg=temp, valid=True)] else: return [Item(u'Set temperature to %.1f°%s' % (float(temp), units), arg=temp, valid=True)] else: if self.nest.mode == 'range': item = Item( u'Target temperature range is %.1f°%s - ' u'%.1f°%s' % (target[0], units, target[1], units)) else: item = Item( u'Target temperature: %.1f°%s' % (target, units)) if self.nest.mode == 'range': item.subtitle = (u'Enter a temperature range in °%s to update; ' u'use format "low high"' % units) else: item.subtitle = u'Enter a temperature in °%s to update' % units return [item] def do_target(self, temp): '''Set the target temperature''' LOG.debug('doing target temperature') temp = temp.strip() if ' ' in temp: temp = temp.split() self.nest.target_temperature = temp units = self.nest.scale.upper() if isinstance(temp, list): self.puts(u'Target temperature range is now %s°%s - ' u'%s°%s' % (temp[0], units, temp[1], units)) else: self.puts(u'Target temperature set to %s°%s' % (temp, units)) def tell_status(self, ignore): '''Tell the Nest's overall status''' LOG.debug('telling status') temp = self.nest.temperature target = self.nest.target_temperature humidity = self.nest.humidity away = 'yes' if self.nest.structure.away else 'no' fan = self.nest.fan units = self.nest.scale.upper() item = Item(u'Temperature: %.1f°%s' % (temp, units)) if self.nest.mode == 'range': target = u'Heat/cool to %.1f°%s - %.1f°%s' % ( target[0], units, target[1], units) elif self.nest.mode == 'heat': target = u'Heating to %.1f°%s' % (target, units) else: target = u'Cooling to %.1f°%s' % (target, units) item.subtitle = u'%s Humidity: %.1f%% Fan: %s Away: %s' % ( target, humidity, fan, away) return [item] def tell_fan(self, ignore): '''Tell the Nest's fan mode''' LOG.debug('telling fan') subtitle = 'Press enter to switch ' if self.nest.fan == 'auto': msg = 'Fan is in auto mode' subtitle += 'on' arg = 'on' else: msg = 'Fan is on' subtitle += 'to auto mode' arg = 'auto' item = Item(msg, valid=True, arg=arg, subtitle=subtitle) return [item] def do_fan(self, mode): '''Set the Nest's fan mode''' LOG.debug('doing fan') if mode not in ('on', 'auto'): raise Exception('Invalid input') self.nest.fan = mode if self.nest.fan == 'auto': self.puts('Fan is in auto mode') else: self.puts('Fan is on') def tell_away(self, ignore): '''Tell the Nest's "away" status''' LOG.debug('telling away') if self.nest.structure.away: msg = "Nest thinks you're away" arg = 'off' else: msg = "Nest thinks you're at home" arg = 'on' item = Item(msg, valid=True, arg=arg, subtitle='Press enter to toggle') return [item] def do_away(self, val): '''Set the Nest's "away" status''' LOG.debug('doing away') away = None if val: val = val.lower() if val in ('on', 'yes', 'true', '1'): val = True elif val in ('off', 'no', 'false', '0'): val = False else: raise Exception('Invalid input') self.nest.structure.away = val away = val else: away = self.nest.structure.away if away: self.puts('Away mode is enabled') else: self.puts('Away mode is disabled') def tell_weather(self, ignored): '''Tell the current weather and a short forecast''' LOG.debug('telling weather') units = self.nest.scale.lower() def new_forecast(title, info): tcond = info['condition'].capitalize() thi = info['temp_high_' + units] tlo = info['temp_low_' + units] item = Item(u'%s: %s' % (title, tcond), subtitle=u'High: {0:.1f}°{1}, ' u'Low: {2:.1f}°{1}'.format( thi, units.upper(), tlo)) return item data = self.nest.structure.weather conditions = data['current']['condition'].capitalize() temp = data['current']['temp_' + units] humidity = data['current']['humidity'] items = [] item = Item(u'Now: %s' % conditions) item.subtitle = u'{0:.1f}°{1}, {2:.0f}% humidity'.format( temp, units.upper(), humidity) items.append(item) items.append(new_forecast('Today', data['forecast']['daily'][0])) items.append(new_forecast('Tomorrow', data['forecast']['daily'][1])) return items def tell_mode(self, query): LOG.debug('telling mode') items = [] for mode in sorted(MODES.keys()): title = MODES[mode]['label'] if mode == self.nest.mode: title += ' (active)' items.append(Item(title, subtitle=MODES[mode]['desc'], arg=mode, valid=True)) if len(query.strip()) > 0: q = query.strip().lower() items = self.fuzzy_match_list(q, items, key=lambda i: i.title.lower()) return items def do_mode(self, mode): LOG.debug('getting mode') self.nest.mode = mode label = MODES[mode]['label'].lower() self.puts('Temperature mode set to %s' % label)