def EventHandler(eventId, eventArg): global owm, apiKey, location if eventId == events.ids.HOURS: # Get weather once/hour if owm == None: apiKey = config.Get("owmApiKey") location = config.Get("owmLocation") if (apiKey != None and location != None): owm = pyowm.OWM(apiKey) # My API key try: obs = owm.weather_at_place(location) # My location except: database.NewEvent(0, "Weather Feed failed!") synopsis.problem("Weather", "Feed failed @ " + str(datetime.now())) return w = obs.get_weather() cloudCover = w.get_clouds() # Percentage cloud cover variables.Set("cloudCover", str(cloudCover), True) outsideTemp = w.get_temperature("celsius")["temp"] # Outside temperature in celsius variables.Set("outsideTemperature", str(outsideTemp), True) windSpeed = w.get_wind()["speed"] variables.Set("windSpeed", str(windSpeed), True) rain = w.get_rain() if rain != {}: rain = 1 # was rain["3h"] # Rain volume in last 3 hours. Unknown units, may be ml(?) else: rain = 0 # No rain variables.Set("rain", str(rain), True) snow = w.get_snow() if snow != {}: snow = 1 # was snow["3h"] # Snow volume in last 3 hours. Unknown units, may be ml(?) else: snow = 0 # No snow variables.Set("snow", str(snow), True) database.NewEvent(0, "Weather now "+str(cloudCover)+"% cloudy") events.Issue(events.ids.WEATHER) # Tell system that we have a new weather report
def testConfig(self): """Tests storage of simple and structured values in Config entities.""" self.assertEquals(None, config.Get('k')) config.Set('k', 'value') self.assertEquals('value', config.Get('k')) config.Set('j', False) self.assertEquals({'k': 'value', 'j': False}, config.GetAll()) config.Set('k', [3, 4, {'a': 'b'}, None]) self.assertEquals([3, 4, {'a': 'b'}, None], config.Get('k')) config.Delete('k') self.assertEquals(None, config.Get('k'))
def EventHandler(eventId, eventArg): global overrideTimeoutMins, currentTargetTemp if eventId == events.ids.MINUTES: name = config.Get("HeatingDevice") if name != None: heatingDevKey = devices.FindDev( name ) # Get heating device every minute to allow for it changing if heatingDevKey != None: # Check that we have a real heating device schedule = config.Get("HeatingSchedule") if schedule != None: scheduledTargetTemp = GetTarget(schedule) if scheduledTargetTemp != None: # We now have a heating device and a schedule to follow or override if overrideTimeoutMins > 0: overrideTimeoutMins = overrideTimeoutMins - 1 if overrideTimeoutMins <= 0: # Just finished override database.NewEvent( heatingDevKey, "Resume " + str(scheduledTargetTemp) + "'C") # For ActivityLog on web page currentTargetTemp = heating.SetTargetTemp( heatingDevKey, scheduledTargetTemp ) # Resume schedule here else: # Still overriding if scheduledTargetTemp != currentTargetTemp: # Check whether schedule in heating device is about to change target database.NewEvent( heatingDevKey, "Overriding scheduled " + str(scheduledTargetTemp) + "'C with " + str(currentTargetTemp) + "C") # For ActivityLog on web page # Un-indent following line to force override temp once/min while overriding, rather than just at change heating.SetTargetTemp( heatingDevKey, currentTargetTemp ) # Re-Set target in heating device (since it's also running the schedule) else: # Not overriding if scheduledTargetTemp != currentTargetTemp: database.NewEvent( heatingDevKey, "Scheduled " + str(scheduledTargetTemp) + "'C") # For ActivityLog on web page #heating.SetTargetTemp(heatingDevKey, scheduledTargetTemp) # Set target in heating device here. (Not needed since it's running the schedule directly) currentTargetTemp = scheduledTargetTemp # else: No scheduled target # else: No HeatingSchedule # else: Despite having a name, there's no associated device variables.Set("TargetTemp", str(currentTargetTemp)) else: # Ignore schedules and overrides if no named heating device synopsis.problem( "NoHeatingDevice", "No HeatingDevice entry in config, needed to resume after override" )
def UpdateMetadata(address): """Updates the cached metadata dictionary for a single source.""" if config.Get('metadata_max_megabytes_per_day_per_source', 50) == 0: logging.info('Skipped; metadata_max_megabytes_per_day_per_source is 0') return fetch_time = time.time() metadata = METADATA_CACHE.Get(address) metadata = FetchAndUpdateMetadata(metadata, address) metadata['fetch_time'] = fetch_time METADATA_CACHE.Set(address, metadata) logging.info('Updated metadata for source: %s %r', address, metadata) if config.Get('metadata_fetch_log'): MetadataFetchLog.Log(address, metadata)
def Get(self, label, domain=None): # pylint: disable=g-bad-name """Displays a published map by its domain and publication label.""" domain = domain or config.Get('primary_domain') or '' entry = model.CatalogEntry.Get(domain, label) if not entry: # Fall back to the map list for users that go to /crisismap/maps. # TODO(kpy): Remove this when the UI has a way to get to the map list. if label == 'maps': return self.redirect('.maps') raise base_handler.Error(404, 'Label %s/%s not found.' % (domain, label)) cm_config = GetConfig(self.request, catalog_entry=entry, xsrf_token=self.xsrf_token) map_root = cm_config.get('map_root', {}) # SECURITY NOTE: cm_config_json is assumed to be safe JSON, and head_html # is assumed to be safe HTML; all other template variables are autoescaped. # Below, we use cm_config.pop() for template variables that aren't part of # the API understood by google.cm.Map() and don't need to stay in cm_config. self.response.out.write(self.RenderTemplate('map.html', { 'maps_api_url': cm_config.pop('maps_api_url', ''), 'head_html': cm_config.pop('custom_head_html', ''), 'lang': cm_config['lang'], 'lang_lower': cm_config['lang'].lower().replace('-', '_'), 'json_proxy_url': cm_config['json_proxy_url'], 'maproot_url': cm_config.pop('maproot_url', ''), 'map_title': map_root.get('title', '') + ' | Google Crisis Map', 'map_description': ToPlainText(map_root.get('description')), 'map_url': self.request.path_url, 'map_image': map_root.get('thumbnail_url', ''), 'cm_config_json': base_handler.ToHtmlSafeJson(cm_config) }))
def RenderTemplate(self, template_name, context): """Renders a template from the templates/ directory. Args: template_name: A string, the filename of the template to render. context: An optional dictionary of template variables. A few variables are automatically added to this context: - {{root}} is the root_path of the app - {{user}} is the signed-in user - {{login_url}} is a URL to a sign-in page - {{logout_url}} is a URL that signs the user out - {{navbar}} contains variables used by the navigation sidebar Returns: A string, the rendered template. """ path = os.path.join(os.path.dirname(__file__), 'templates', template_name) root = config.Get('root_path') or '' user = users.GetCurrent() context = dict(context, root=root, user=user, xsrf_tag=self.xsrf_tag, login_url=users.GetLoginUrl(self.request.url), logout_url=users.GetLogoutUrl(root + '/.maps'), navbar=self._GetNavbarContext(user)) return template.render(path, context)
def GetMapPickerItems(domain, root_path): """Fetches the list of maps to show in the map picker menu for a given domain. Args: domain: A string, the domain whose catalog to fetch. root_path: The relative path to the Crisis Map site root. Returns: A list of {'title': ..., 'url': ...} dictionaries describing menu items corresponding to the CatalogEntry entities for the specified domain. """ map_picker_items = [] # Add menu items for the CatalogEntry entities that are marked 'listed'. if domain: if domain == config.Get('primary_domain'): map_picker_items = [ {'title': entry.title, 'url': root_path + '/' + entry.label} for entry in list(model.CatalogEntry.GetListed(domain))] else: map_picker_items = [ {'title': entry.title, 'url': root_path + '/%s/%s' % (entry.domain, entry.label)} for entry in list(model.CatalogEntry.GetListed(domain))] # Return all the menu items sorted by title. return sorted(map_picker_items, key=lambda m: m['title'])
def GetPlacesApiResults(base_url, request_params, result_key_name=None): """Fetches results from Places API given base_url and request params. Args: base_url: URL prefix to use before the request params request_params: An array of key and value pairs for the request result_key_name: Name of the results field in the Places API response or None if the whole response should be returned Returns: Value for the result_key_name in the Places API response or all of the response if result_key_name is None """ google_api_server_key = config.Get('google_api_server_key') if not google_api_server_key: raise base_handler.Error( 500, 'google_api_server_key is not set in the config') request_params += [('key', google_api_server_key)] url = base_url + urllib.urlencode([(k, v) for k, v in request_params if v]) # Call Places API if cache doesn't have a corresponding entry for the url def GetPlacesJson(): response = urlfetch.fetch(url=url, deadline=DEADLINE) return json.loads(response.content) response_content = JSON_PLACES_API_CACHE.Get(url, GetPlacesJson) # Parse results status = response_content.get('status') if status != 'OK' and status != 'ZERO_RESULTS': # Something went wrong with the request, log the error logging.error('Places API request [%s] failed with error %s', url, status) return [] return (response_content.get(result_key_name) if result_key_name else response_content)
def SetSunTimes(): cityName = config.Get("cityName") if cityName != None: city = LocationInfo(cityName) s = sun(city.observer, date=datetime.now()) variables.Set("dawn", str(s['dawn'].strftime("%H:%M")), True) variables.Set("sunrise", str(s['sunrise'].strftime("%H:%M")), True) variables.Set("sunset", str(s['sunset'].strftime("%H:%M")), True) variables.Set("dusk", str(s['dusk'].strftime("%H:%M")), True)
def CheckAccess(self): """If login_access_list is set, accept only the specified logins.""" login_access_list = config.Get('login_access_list') if login_access_list is not None: user = users.GetCurrent() if not user: raise RedirectToUrl(users.GetLoginUrl(self.request.url)) if user.email not in login_access_list: raise perms.AuthorizationError(user, None, None)
def Get(self, label, topic_id, user=None, domain=None): domain = domain or config.Get('primary_domain') or '' entry = model.CatalogEntry.Get(domain, label) if not entry: logging.severe('No map with label %s under domain %s' % (label, domain)) raise base_handler.Error(404, 'No such map.') self.GetForMap(entry.map_root, entry.map_version_id, topic_id, label, domain)
def Get(self, label, user=None, domain=None): domain = domain or config.Get('primary_domain') or '' entry = model.CatalogEntry.Get(domain, label) if not entry: raise base_handler.Error(404, 'No such map.') topics = entry.map_root.get('topics', []) if not topics: raise base_handler.Error(404, 'Map has no topics.') self.redirect('%s/%s' % (label, str(topics[0]['id'])))
def MakeText(id): if weather.forecastPeriod=="": return None; reportDict = dict() reportDict["target"] = id minutes = datetime.now().minute #if minutes % 2 == 0: # reportDict["display"] = "off" #else: reportDict["display"] = "on" # For now at least reportDict["period"] = weather.forecastPeriod reportDict["icon"] = str(weather.symSym)[4:] reportDict["cloudText"] = weather.cloudText reportDict["maxTemp"] = str(round(weather.maxTemp))+"C" reportDict["minTemp"] = str(round(weather.minTemp))+"C" reportDict["windSpeed"] = str(round(weather.maxWind)) reportDict["windDir"] = str(weather.windDir) reportDict["windText"] = weather.windText now = datetime.now() reportDict["timeDigits"] = str(now.strftime("%H:%M")) reportDict["timeText"] = GetTimeInWords() reportDict["dayOfWeekText"] = str(now.strftime("%A")) reportDict["dayOfMonthText"] = str(int(now.strftime("%d"))) # Use int() to remove leading zero reportDict["monthText"] = str(now.strftime("%B")) powerMonitorName = config.Get("PowerMonitor") # House power monitoring device if powerMonitorName != None: devKey = devices.FindDev(powerMonitorName) if devKey != None: powerW = database.GetLatestLoggedItem(devKey, "PowerReadingW") if powerW != None: reportDict["powerNow"] = str(powerW[0]) energyToday = variables.Get("energyToday_kWh") if energyToday: reportDict["energyToday"] = energyToday tempMonitorName = config.Get("HouseTempDevice") # House temperature monitoring device if tempMonitorName != None: devKey = devices.FindDev(tempMonitorName) if devKey != None: houseTemp = database.GetLatestLoggedItem(devKey, "TemperatureCelsius") reportDict["houseTemp"] = '%.1f' % (houseTemp[0]) targetTemp = variables.Get("TargetTemp") if targetTemp != None: reportDict["TargetTemp"] = targetTemp #log.debug("ReportDict for:" +id + "=" + str(reportDict)) # Already reported in WiFiServer.py return (str(reportDict))
def DetermineFetchInterval(metadata): """Decides how long to wait before fetching a layer's data again.""" # TODO(kpy): Add overall rate-limiting (total bandwidth across all sources). # By default, fetch each source at most once per minute. min_interval = config.Get('metadata_min_interval_seconds', 60) # By default, on failure, wait at least 10 minutes before trying again. min_interval_after_error = config.Get( 'metadata_min_interval_after_error_seconds', 600) # By default, fetch each source at least once a day. max_interval = config.Get('metadata_max_interval_hours', 24) * 3600 # By default, limit fetch bandwidth to 50 megabytes per day per source. mb_per_day = config.Get('metadata_max_megabytes_per_day_per_source', 50) # Estimate interval based on a metric of cost expended by the remote server. fetch_cost = HTTP_FIXED_COST + metadata.get('fetch_length', 0) interval = int(fetch_cost / (max(mb_per_day, 0.001) * 1e6 / 24 / 3600)) # Also keep the interval within our minimum and maximum bounds. min_seconds = (metadata.get('fetch_error_occurred') and min_interval_after_error or min_interval) return max(min_seconds, min(max_interval, interval))
def ScheduleFetch(address, countdown=None): """Schedules the next fetch task for a source.""" metadata = METADATA_CACHE.Get(address) or {} if not metadata.get('fetch_impossible'): if countdown is None: countdown = DetermineFetchInterval(metadata) logging.info('Scheduling fetch in %ds for source: %s', countdown, address) taskqueue.add(queue_name='metadata', countdown=countdown, method='GET', url=(config.Get('root_path') or '') + '/.metadata_fetch', params={'source': address})
def EventHandler(eventId, eventArg): global ser, txBuf, rxBuf if eventId == events.ids.INIT: serPort = config.Get("tty", '/dev/ttyUSB0') serSpeed = config.Get("baud", '19200') ser = serial.Serial(serPort, int(serSpeed), timeout=0) ser.flushInput() if database.GetDevicesCount()==0: # If we have no devices yet, then... devices.Add("0000", "N/A", "COO") # ...make sure we add this device as the first item before we try to use it! queue.EnqueueCmd(0, ["ATS63=0007", "OK"]) # Request RSSI & LQI on every received message, also disable automatic checkIn responses queue.EnqueueCmd(0, ["ATS0F=0400", "OK"]) # Use 0600 to set bit 9 (bit 10 already set) to get rawzcl responses so we can see schedule responses from thermostat if database.GetDeviceItem(0, "modelName") == None: queue.EnqueueCmd(0, ["ATI", "OK"]) # Request our EUI, as well as our Telegesis version queue.EnqueueCmd(0, ["AT+N", "OK"]) # Get network information, to see whether to start new network or use existing one elif eventId == events.ids.SECONDS: HandleSerial(ser) while len(rxBuf): Parse(rxBuf.popleft()) elif eventId == events.ids.RADIO_INFO: print(ourChannel+","+ourPowLvl+","+ourPan+","+ourExtPan) # Formatted to make it easy to extract in php elif eventId == events.ids.INFO: print("TxBuf: ", str(txBuf))
def ContainsSpam(text): """Checks text for words and phrases that are considered spam. Args: text: The text to be scanned. Returns: True if the text contains any of the words or phrases in the config setting 'crowd_report_spam_phrases', which should be a list of strings. Whitespace is normalized and case is ignored for comparison. """ lowercase_text = ' '.join(text.lower().split()) for spam_phrase in config.Get('crowd_report_spam_phrases', []): lowercase_spam = ' '.join(spam_phrase.lower().split()) if lowercase_spam in lowercase_text: return True
def GetDestination(request, domain_name): """Based on the request, determines the map URL to redirect to.""" # For backward compatibility, support the id= and crisis= parameters. label = request.get('id') or request.get('crisis') if not label: domain = domains.Domain.Get(domain_name or config.Get('primary_domain')) label = domain and domain.default_label or 'empty' if domain_name: url = request.root_path + '/' + domain_name + '/' + label else: url = request.root_path + '/' + label # Preserve all the query parameters except those that set the label. params = dict((key, value) for (key, value) in request.GET.items() if key not in ['id', 'crisis']) return url + (params and '?' + urllib.urlencode(params) or '')
def _GetMap(self, label, domain): """Loads the model.Map instance being reviewed by label and domain. Args: label: A string, the published label for the map. domain: A string, the domain in which the map was created, eg gmail.com. Returns: The model.Map instance being reviewed Raises: base_handler.Error: If the map csnnot be found. """ domain = domain or config.Get('primary_domain') or '' entry = model.CatalogEntry.Get(domain, label) if not entry: raise base_handler.Error(404, 'Map %r not found.' % label) map_object = model.Map.Get(entry.map_id) if not map_object: raise base_handler.Error(404, 'Map %r not found.' % label) return map_object
def Keep(): cfg = config.Get('mysql') conn = New(cfg['host'], cfg['port'], cfg['user'], cfg['passwd'], cfg['name']) return KeepThis('', conn)
if __name__ == "__main__": save_filename = False email = False showalarms = False try: opts, args = getopt.getopt(sys.argv[1:], "", ["hours=", "config=", "file=", "email=", "alarms=",]) except getopt.GetoptError: print ('dashboard.py --config <config yaml> --hours <hours> --email <email> --file <file> --alarms') sys.exit(2) for opt, arg in opts: if opt in ("-h", "--hours"): hours = int(arg) elif opt in ("-c", "--config"): config.Get(config=arg) elif opt in ("-f", "--file"): save_filename = arg elif opt in ("-e", "--email"): email = arg elif opt in ("-a", "--alarms"): showalarms = arg boto3.setup_default_session( aws_access_key_id=config.cfg['Credentials']['AWS_ID'], aws_secret_access_key=config.cfg['Credentials']['AWS_PASS'], region_name=config.cfg['Credentials']['region'] ) graphs = [] for graphs_list in config.cfg["Graphs"]:
def main(filename): doSideboard = config.Get('options', 'display_sideboard') #open user input decklist raw_decklist = open(str(filename), 'r') deck_list = decklist.parse_list(raw_decklist) raw_decklist.close() print(repr(deck_list)) nstep = 1 # create a header with the deck's name global fnt if deck_list.game == decklist.MTG: fnt = ImageFont.truetype( os.path.join(globals.RESOURCES_PATH, 'fonts', 'belerensmallcaps-bold-webfont.ttf'), 14) fnt_title = ImageFont.truetype( os.path.join(globals.RESOURCES_PATH, 'fonts', 'belerensmallcaps-bold-webfont.ttf'), 18) title = Image.new("RGB", (280, 34), "black") drawtitle = ImageDraw.Draw(title) drawtitle.text((10, 7), os.path.basename(str(filename))[0:-4], (250, 250, 250), font=fnt_title) elif deck_list.game == decklist.POKEMON: fnt = ImageFont.truetype( os.path.join(globals.RESOURCES_PATH, 'fonts', 'ufonts.com_humanist521bt-ultrabold-opentype.otf'), 10) fnt_title = ImageFont.truetype( os.path.join(globals.RESOURCES_PATH, 'fonts', 'ufonts.com_humanist521bt-ultrabold-opentype.otf'), 14) title = Image.new("RGB", (219, 35), "black") drawtitle = ImageDraw.Draw(title) drawtitle.text((10, 8), os.path.basename(str(filename))[0:-4], (250, 250, 250), font=fnt_title) elif deck_list.game == decklist.HEX: fnt = ImageFont.truetype( os.path.join(globals.RESOURCES_PATH, 'fonts', 'Arial Bold.ttf'), 16) fnt_title = ImageFont.truetype( os.path.join(globals.RESOURCES_PATH, 'fonts', 'Arial Bold.ttf'), 18) title = Image.new("RGB", (320, 34), "black") nametitle = str(filename)[0:-4] nshard = 0 for shard in [ '[DIAMOND]', '[SAPPHIRE]', '[BLOOD]', '[RUBY]', '[WILD]' ]: #print nametitle,nshard if nametitle.find(shard) != -1: nametitle = nametitle.replace(shard, '') newshard = Image.open( os.path.join(globals.RESOURCES_PATH, 'mana', shard + '.png')).resize((20, 20)) title.paste(newshard, (10 + nshard * 20, 7)) nshard = nshard + 1 drawtitle = ImageDraw.Draw(title) drawtitle.text((15 + nshard * 20, 12), os.path.basename(nametitle), (250, 250, 250), font=fnt_title) ncountMB = len(deck_list.mainboard) ncountSB = len(deck_list.sideboard) ncount = ncountMB if ncountSB == 0: doSideboard = False if doSideboard: #create a Sideboard partition sideboard = Image.new("RGB", (280, 34), "black") drawtitle = ImageDraw.Draw(sideboard) sideboard_name = "Sideboard" if deck_list.game == decklist.HEX: sideboard_name = "Reserves" drawtitle.text((10, 7), sideboard_name, (250, 250, 250), font=fnt_title) ncount += ncountSB + 1 #define the size of the canvas, incl. space for the title header if deck_list.game == decklist.MTG: deckwidth = 280 deckheight = 34 * (ncount + 1) #for scrolling decklist deckwidth2 = 270 * (ncount + 1) deckheight2 = 34 elif deck_list.game == decklist.POKEMON: deckwidth = 219 deckheight = 35 * (ncount + 1) elif deck_list.game == decklist.HEX: deckwidth = 320 deckheight = 35 * (ncount + 1) #reset the sideboard marker isSideboard = 0 global deck deck = Image.new("RGB", (deckwidth, deckheight), "white") #for scrolling decklist global deck2 deck2 = Image.new("RGB", (deckwidth2, deckheight2), "white") deck.paste(title, (0, 0)) #for scrolling decklist title2 = title.crop((0, 0, 270, 34)) deck2.paste(title2, (0, 0)) #now read the decklist if deck_list.game == decklist.MTG: lands = [] for card in deck_list.mainboard: #this step checks whether a specific art is requested by the user - provided via the set name if card.cost == "*": lands.append(card) continue draw_mtg_card(card, nstep) nstep = nstep + 1 for card in lands: draw_mtg_card(card, nstep) nstep = nstep + 1 if doSideboard: deck.paste(sideboard, (0, 34 * nstep)) #for scrolling decklist sideboard2 = sideboard.crop((0, 0, 270, 34)) deck2.paste(sideboard2, (270 * nstep, 0)) nstep = nstep + 1 for card in deck_list.sideboard: draw_mtg_card(card, nstep) nstep = nstep + 1 elif deck_list.game == decklist.POKEMON: for card in deck_list.mainboard: quantity = card.quantity lookupScan, displayname = scraper.download_scanPKMN( card.name, card.set, card.collector_num) img = Image.open(lookupScan) #check if im has Alpha band... if img.mode != 'RGBA': img = img.convert('RGBA') #resize the gradient to the size of im... alpha = gradient.resize(img.size) #put alpha in the alpha band of im... img.putalpha(alpha) bkgd = Image.new("RGB", img.size, "black") bkgd.paste(img, (0, 0), mask=img) cut = bkgd.crop( (xtopPKMN, ytopPKMN + 90, xbotPKMN - 10, ybotPKMN + 100)) cut = cut.resize((deckwidth, 34)) draw = ImageDraw.Draw(cut) #create text outline draw.text((6, 11), str(quantity) + ' ' + displayname, (0, 0, 0), font=fnt) draw.text((8, 11), str(quantity) + ' ' + displayname, (0, 0, 0), font=fnt) draw.text((6, 13), str(quantity) + ' ' + displayname, (0, 0, 0), font=fnt) draw.text((8, 13), str(quantity) + ' ' + displayname, (0, 0, 0), font=fnt) #enter text draw.text((7, 12), str(quantity) + ' ' + displayname, (250, 250, 250), font=fnt) #place the cropped picture of the current card deck.paste(cut, (0, 35 * nstep)) nstep = nstep + 1 elif deck_list.game == decklist.HEX: banner = Image.new("RGB", (deckheight - 35, 50), "black") if len(deck_list.commander) > 0: cmdr = deck_list.commander[0] guid = cmdr.collector_num typeCM = cmdr.set drawbanner = ImageDraw.Draw(banner) drawbanner.text((15, 15), str(cmdr.name), (250, 250, 250), font=fnt_title) lookupScan = scraper.download_scanHexCM(cmdr.name, guid, typeCM) mainguyImg = Image.open(lookupScan) mainguycut = mainguyImg.crop((135, 55, 185, 275)) banner = banner.rotate(90, expand=True) #check if im has Alpha band... if mainguycut.mode != 'RGBA': mainguycut = mainguycut.convert('RGBA') #resize the gradient to the size of im... alpha = Hexgradient.resize(mainguycut.size) #put alpha in the alpha band of im... mainguycut.putalpha(alpha) banner.paste(mainguycut, (0, 0), mask=mainguycut) deck.paste(banner, (0, 35)) for card in deck_list.mainboard: draw_hex_card(card.name, card.collector_num, card.quantity, nstep) nstep = nstep + 1 if doSideboard: deck.paste(sideboard, (50, 35 * nstep)) nstep = nstep + 1 for card in deck_list.sideboard: draw_hex_card(card.name, card.collector_num, card.quantity, nstep) nstep = nstep + 1 if deck_list.game == decklist.MTG: deck = deck.crop((0, 0, deckwidth - 10, deckheight)) deck2 = deck2.crop((0, 0, deckwidth2, deckheight2 - 2)) elif deck_list.game == decklist.POKEMON: deck = deck.crop((0, 0, deckwidth - 10, 35 * nstep)) elif deck_list.game == decklist.HEX: deck = deck.crop((0, 0, deckwidth - 22, deckheight)) output_path = str(filename)[0:-4] + ".png" deck.save(output_path) #for scrolling decklist output_path2 = str(filename)[0:-4] + "-scroll.png" deck2.save(output_path2) altpath = config.Get('options', 'output_path') if altpath is not None: deck.save(altpath) return output_path
"lastblock": "524e347f08e65c7d23bb7a24e6fae828f22db8a1d198bbe11aea655c18015a91" } return listsinceblock class MainHandler(web.RequestHandler): async def get(self): self.write("JSONRPC server handles only POST requests") async def post(self): request = self.request.body.decode() if rpc_config.verbose > 1: print(request) response = await methods.dispatch(request) if not response.is_notification: self.write(response) # see http://www.tornadoweb.org/en/stable/httpserver.html#http-server for ssl # see http://www.tornadoweb.org/en/stable/web.html#tornado.web.Application.settings for logging and such app = web.Application([(r"/", MainHandler)]) if __name__ == "__main__": rpc_config = config.Get() app.listen(rpc_config.rpcport) ioloop.IOLoop.current().start()
def Action(actList, ruleId): log.debug("Action with: " + str(actList)) action = actList[0].lower() if action == "Log".lower(): log.debug("Rule says Log event for " + ' '.join(actList[1:])) elif action == "Play".lower(): call(["omxplayer", "-o", actList[1], actList[2]]) elif action == "Event".lower(): if actList[1].lower() == "TimeOfDay".lower(): events.IssueEvent(events.ids.TIMEOFDAY, actList[2]) elif actList[1].lower() == "Alarm".lower(): events.IssueEvent(events.ids.ALARM, actList[2]) # Could have other events here... elif action == "synopsis": # Was status emailAddress = config.Get("emailAddress") log.debug("About to send synopsis to " + emailAddress) if emailAddress != None: synopsis.BuildPage() # Create synopsis page on demand with open("synopsis.txt", "r") as fh: # Plain text of email emailText = fh.readlines() text = ''.join(emailText) with open("synopsis.html", "r") as fh: # HTML of email emailHtml = fh.readlines() html = ''.join(emailHtml) sendmail.email("Vesta Status", text, html) # See sendmail.py else: synopsis.problem( "NoEmail", "No emailAddress entry in config, needed to send synopsis") elif action == "email": # All args are body of the text. Fixed subject and email address emailAddress = config.Get("emailAddress") if emailAddress != None: emailBody = [] for item in actList[1:]: emailBody.append(item) plainText = " ".join(emailBody) log.debug("Sending email with '" + plainText + "'") result = sendmail.email("Vesta Alert!", plainText, None) if result != 0: synopsis.problem( "Email", "sendmail.email() failed with code " + str(result) + " when trying to send:" + plainText) else: synopsis.problem("NoEmail", "No emailAddress entry in config") elif action == "override": # Syntax is "Override <targetDevice> <targetDegC> <durationSecs>" devKey = devices.FindDev(actList[1]) target = actList[2] timeSecs = actList[3] if devKey != None: schedule.Override(devKey, target, timeSecs) elif action == "set": # Set a named variable to a value expression = "".join( actList[1:] ) # First recombine actList[1] onwards, with no spaces. Now expression should be of the form "<var>=<val>" if "--" in expression: sep = expression.index("--") varName = expression[:sep] varVal = variables.Get(varName) if isNumber(varVal): newVal = str(eval(varVal + "-1")) variables.Set(varName, newVal) Run( varName + "==" + newVal ) # Recurse! to see if any rules need running now that we've set a variable else: log.fault(varName + " not a number at " + expression) elif "++" in expression: sep = expression.index("++") varName = expression[:sep] varVal = variables.Get(varName) if isNumber(varVal): newVal = str(eval(varVal + "+1")) variables.Set(varName, newVal) Run( varName + "==" + newVal ) # Recurse! to see if any rules need running now that we've set a variable else: log.fault(varName + " not a number at " + expression) elif "=" in expression: sep = expression.index("=") varName = expression[:sep] varVal = expression[sep + 1:] variables.Set(varName, varVal) Run( varName + "==" + varVal ) # Recurse! to see if any rules need running now that we've set a variable else: log.fault("Badly formatted rule at " + expression) elif action == "unset": # Remove a named variable variables.Del(actList[1]) else: # Must be a command for a device, or group of devices if len(actList) >= 2: # Check that we have a second arg... name = actList[1] # Second arg is name if database.IsGroupName(name): # Check if name is a groupName devKeyList = GetGroupDevs(name) for devKey in devKeyList: CommandDev(action, devKey, actList, ruleId) # Command each device in list else: devKey = database.GetDevKey("userName", name) CommandDev(action, devKey, actList, ruleId) # Command one device
def BuildPage(): upTime = datetime.now() - iottime.appStartTime absUrl = config.Get("vestaURL", "") log.debug("Building status page") txt = open("synopsis.txt", "w") # Create text file for txt html = open( "synopsis.html", "w" ) # Create local html file, so we can copy it for Apache to serve up, or mail directly html.write("\n<html><head>") # Blank line at start html.write( "<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE, no-store, must-revalidate\">" ) # Try to force browser to discard any previous version of this page html.write("</head><body>") html.write("<center><h1>Vesta Status</h1>") txt.write("Vesta Status\n\n") writeLine( "At " + datetime.now().strftime("%H:%M") + " on " + datetime.now().strftime("%Y/%m/%d"), html, txt) writeLine("Uptime: %d days, %.2d:%.2d" % (upTime.days, upTime.seconds // 3600, (upTime.seconds // 60) % 60), html, txt) # Cribbed from "uptime" command writeLine("", html, txt) # Just newline keyList = database.GetAllDevKeys( ) # Get a list of all the device identifiers from the database noProblems = True restartCount = database.CountEvents(0, "App started", "date('now', '-1 days')") if restartCount > 0: noProblems = False writeLine( "App restarted " + str(restartCount) + " times in the last 24 hours", html, txt) else: # Don't check availablilty of devices if app has restarted for devKey in keyList: # Element 0 is hub, rest are devices if database.GetDeviceItem(devKey, "nwkId") != "0000": # Ignore hub availability = presence.GetAvailability(devKey) if availability != "": # If device missing even only occasionally, tell user (Empty string means "fine") noProblems = False writeLine(availability, html, txt) dbSize = database.GetFileSize() if (dbSize / len(keyList)) > (30 * 1024): # Arbitrary limit of 30K per device noProblems = False problem( "dbSize", "Database file size is " + "{0:.2f}".format(dbSize / (1024 * 1024)) + "MB which is " + "{0:.0f}".format((dbSize / len(keyList)) / 1024) + "KB per device") errList = glob.glob( "/home/pi/Vesta/*_err.log") # Get list of all error logs numLogs = len(errList) if numLogs: noProblems = False if numLogs == 1: problem("error_logs", "1 error log") else: problem("error_logs", str(numLogs) + " error logs") if len(issues) > 0: noProblems = False for items in issues.values(): writeLine(items, html, txt) if noProblems: writeLine("Everything OK!", html, txt) writeLine("", html, txt) # Just newline writeLine("(Vesta v" + vesta.GetVersion() + ")", html, txt) html.write("<br><center><a href=\"" + absUrl + "/vesta/index.php\"><img src=\"" + absUrl + "/vesta/vestaLogo.png\" width=32 height=32 title=\"Home\"></a>") html.write("</body></html>") html.close() txt.close() os.system( "sudo cp synopsis.html /var/www/html/vesta" ) # So vesta.php can refer to it. Will overrwrite any previous status
def GetConfig(request, map_object=None, catalog_entry=None, xsrf_token=''): dev_mode = request.get('dev') and users.IsDeveloper() map_picker_items = GetMapPickerItems( catalog_entry and catalog_entry.domain or config.Get('primary_domain'), request.root_path) # Fill the cm_config dictionary. root = request.root_path xsrf_qs = '?xsrf_token=' + xsrf_token # needed for all POST URLs result = { 'dev_mode': dev_mode, 'langs': base_handler.ALL_LANGUAGES, # Each endpoint that the JS client code uses gets an entry in config. 'js_root': root + '/.js', 'json_proxy_url': root + '/.jsonp', 'kmlify_url': request.host_url + root + '/.kmlify', 'login_url': users.GetLoginUrl(request.url), 'logout_url': users.GetLogoutUrl(request.url), 'map_picker_items': map_picker_items, 'protect_url': root + '/.protect', 'report_query_url': root + '/.api/reports', 'report_post_url': root + '/.api/reports' + xsrf_qs, 'vote_post_url': root + '/.api/votes' + xsrf_qs, 'static_content_url': root + '/.static', 'user_email': users.GetCurrent() and users.GetCurrent().email, 'wms_configure_url': root + '/.wms/configure', 'wms_tiles_url': root + '/.wms/tiles' } # Add settings from the selected client config, if any. result.update(GetClientConfig(request.get('client'), request.headers.get('referer'), dev_mode)) # Add the MapRoot data and other map-specific information. if catalog_entry: # published map map_root = result['map_root'] = catalog_entry.map_root result['label'] = catalog_entry.label result['publisher_name'] = catalog_entry.publisher_name key = catalog_entry.map_version_key elif map_object: # draft map map_root = result['map_root'] = map_object.map_root result['map_list_url'] = root + '/.maps' result['diff_url'] = root + '/.diff/' + map_object.id + xsrf_qs result['save_url'] = root + '/.api/maps/' + map_object.id + xsrf_qs result['share_url'] = root + '/.share/' + map_object.id + xsrf_qs result['api_maps_url'] = root + '/.api/maps' result['legend_url'] = root + '/.legend' result['wms_query_url'] = root + '/.wms/query' result['enable_editing'] = map_object.CheckAccess(perms.Role.MAP_EDITOR) result['draft_mode'] = True key = map_object.current_version_key # Parameters that depend on the MapRoot, for both published and draft maps. ui_region = request.get('gl') if map_object or catalog_entry: result['lang'] = base_handler.SelectLanguageForRequest(request, map_root) ui_region = map_root.get('region', ui_region) cache_key, sources = metadata.CacheSourceAddresses(key, result['map_root']) result['metadata'] = {s: METADATA_CACHE.Get(s) for s in sources} result['metadata_url'] = root + '/.metadata?ck=' + cache_key metadata.ActivateSources(sources) # Construct the URL for the Maps JavaScript API. api_url_params = { 'sensor': 'false', 'libraries': 'places,search,visualization,weather', 'client': GetMapsApiClientId(request.host), 'language': request.lang } if ui_region: api_url_params['region'] = ui_region result['maps_api_url'] = (MAPS_API_BASE_URL + '?' + urllib.urlencode(api_url_params)) maproot_url = request.get('maproot_url', '') if dev_mode or maproot_url.startswith(request.root_url + '/'): # It's always okay to fetch MapRoot JSON from a URL if it's from this app. # In developer mode only, allow MapRoot JSON from arbitrary URLs. result['maproot_url'] = maproot_url if dev_mode: # In developer mode only, allow query params to override the result. # Developers can also specify map_root directly as a query param. for name in ( ClientConfig.properties().keys() + ['map_root', 'use_tab_panel']): value = request.get(name) if value: result[name] = json.loads(value) return result
def HandleRequest(self, **kwargs): """A wrapper around the Get or Post method defined in the handler class.""" try: method = getattr(self, self.request.method.capitalize(), None) root_path = config.Get('root_path') or '' user = users.GetCurrent() if not method: raise Error(405, '%s method not allowed.' % self.request.method) # Enforce login restrictions. self.CheckAccess() # Set self.auth according to the API key in the request, if specified. self.auth = GetAuthForRequest(self.request) # Require/allow domain name and user sign-in based on whether the method # takes arguments named 'domain' and 'user'. args, _, _, defaults = inspect.getargspec(method) required_args = args[:len(args) - len(defaults or [])] if 'domain' in kwargs and 'domain' not in args: raise Error(404, 'Not found.') if 'domain' in required_args and 'domain' not in kwargs: raise Error(400, 'Domain not specified.') if 'user' in args: kwargs['user'] = user if 'user' in required_args and not user: return self.redirect(users.GetLoginUrl(self.request.url)) # Prepare an XSRF token if the user is signed in. if user: self.xsrf_token = GenerateXsrfToken(user.id) self.xsrf_tag = ( '<input type="hidden" name="xsrf_token" value="%s">' % self.xsrf_token) # Require a valid XSRF token for all authenticated POST requests. if user and self.request.method == 'POST': xsrf_token = self.request.get('xsrf_token', '') if not ValidateXsrfToken(user.id, xsrf_token): logging.warn('Bad xsrf_token %r for uid %r', xsrf_token, user.id) # The window might have been idle for a day; go somewhere reasonable. return self.redirect(root_path + '/.maps') # Fill in some useful request variables. self.request.lang = SelectLanguage( self.request.get('hl'), self.request.headers.get('accept-language')) self.request.root_path = root_path self.request.root_url = self.request.host_url + root_path # To prevent clickjacking attacks, disable framing by default. if not self.embeddable: self.response.headers['X-Frame-Options'] = 'DENY' # Call the handler, making nice pages for errors derived from Error. method(**kwargs) except RedirectToUrl as exception: return self.redirect(exception.url) except perms.AuthorizationError as exception: self.response.set_status(403, message=exception.message) self.response.out.write( self.RenderTemplate( 'unauthorized.html', { 'exception': exception, 'login_url': users.GetLoginUrl(self.request.url) })) except perms.NotPublishableError as exception: self.response.set_status(403, message=exception.message) self.response.out.write( self.RenderTemplate(self.error_template, {'exception': exception})) except perms.NotCatalogEntryOwnerError as exception: # TODO(kpy): Either add a template for this type of error, or use an # error representation that can be handled by one common error template. self.response.set_status(403, message=exception.message) self.response.out.write( self.RenderTemplate( self.error_template, { 'exception': utils.Struct( message='That publication label is owned ' 'by someone else; you can\'t replace or delete it.' ) })) except ApiError as exception: self.response.set_status(exception.status, message=exception.message) self.response.headers['Content-Type'] = 'text/plain' self.response.out.write(exception.message + '\n') except Error as exception: self.response.set_status(exception.status, message=exception.message) self.response.out.write( self.RenderTemplate(self.error_template, {'exception': exception}))
def EventHandler(eventId, eventArg): global ephemera, globalDevKey, pendingBinding, pendingBindingTimeoutS, pendingRptAttrId, msp_ota if eventId == events.ids.PREINIT: keyList = database.GetAllDevKeys( ) # Get a list of all the device identifiers from the database for i in range( 100): # Fudge to ensure we have enough pendingBinding entries pendingBinding.append( "" ) # Problem is that the devKey indices aren't consecutive (removed devices) and so pendingBindingTimeoutS.append( 0 ) # using the keyList isn't the same as for x in range(maxDevKey) for devKey in keyList: # Hub and devices Init(devKey) # Initialise dictionary and associated ephemera if database.GetDeviceItem(devKey, "nwkId") != "0000": # Ignore hub SetTempVal( devKey, "GetNextBatteryAfter", datetime.now()) # Ask for battery shortly after startup if eventId == events.ids.INIT: msp_ota = config.Get("MSP_OTA") if eventId == events.ids.DEVICE_ANNOUNCE: if len(eventArg) >= 3: eui64 = eventArg[1] nwkId = eventArg[2] devKey = GetKey(nwkId) if devKey == None: # Which will only be the case if we've not seen this short Id before devKey = database.GetDevKey("eui64", eui64) if devKey == None: # Which will be the case if we've not seen the long Id either devKey = Add(nwkId, eui64, eventArg[0]) log.debug("New key for new device is " + str(devKey)) if eventArg[0] == "SED": SetTempVal(devKey, "PollingUntil", datetime.now() + timedelta(seconds=300)) events.Issue( events.ids.NEWDEVICE, devKey ) # Tell everyone that a new device has been seen, so it can be initialised else: # Same long Id, but short Id needs updating after it has changed database.SetDeviceItem(devKey, "nwkId", nwkId) else: NoteMsgDetails(devKey, eventArg) SetTempVal( devKey, "GetNextBatteryAfter", datetime.now() ) # Ask for battery shortly after Device Announce, either new or old one re-joining if eventId == events.ids.CHECKIN: # See if we have anything to ask the device... if len(eventArg) >= 3: endPoint = eventArg[2] seq = "00" # was seq = eventArg[3], but that's the RSSI devKey = GetKey(eventArg[1]) if devKey != None: EnsureInBinding( devKey, zcl.Cluster.PollCtrl ) # Assume CheckIn means PollCtrl must be in binding, so make sure this is up-to-date NoteMsgDetails(devKey, eventArg) if database.GetDeviceItem(devKey, "endPoints") == None: database.SetDeviceItem( devKey, "endPoints", endPoint ) # Note endpoint that CheckIn came from, unless we already know this nwkId = database.GetDeviceItem(devKey, "nwkId") cmdRsp = Check( devKey ) # Check to see if we want to know anything about the device if cmdRsp != None: log.debug("Keep awake for 10 secs so we can send " + cmdRsp[0]) queue.Jump(devKey, [ "AT+RAWZCL:" + nwkId + "," + endPoint + ",0020,11" + seq + "00012800", "DFTREP" ]) # Tell device to enter Fast Poll for 40qs (==10s) SetTempVal(devKey, "PollingUntil", datetime.now() + timedelta(seconds=10)) queue.EnqueueCmd( devKey, cmdRsp) # This will go out after the Fast Poll Set else: SetTempVal( devKey, "PollingUntil", datetime.now() + timedelta(seconds=2) ) # Say that it's polling for a short while, so that we can tell it to stop(!) queue.EnqueueCmd(devKey, [ "AT+RAWZCL:" + nwkId + "," + endPoint + ",0020,11" + seq + "00000100", "DFTREP" ]) # Tell device to stop Poll else: # Unknown device, so assume it's been deleted from our database telegesis.Leave( eventArg[1] ) # Tell device to leave the network, since we don't know anything about it if eventId == events.ids.TRIGGER or eventId == events.ids.BUTTON: if len(eventArg) >= 2: devKey = GetKey( eventArg[1] ) # Lookup device from network address in eventArg[1] if devKey != None: SetTempVal( devKey, "PollingUntil", datetime.now() + timedelta(seconds=1) ) # Say that it's polling for a very short while so that we can try to set up a PollCtrl cluster if eventId == events.ids.RXMSG: if eventArg[0] == "AddrResp" and eventArg[1] == "00" and len( eventArg) >= 3: devKey = GetKey(eventArg[2]) if devKey != None: database.SetDeviceItem(devKey, "eui64", eventArg[1]) elif eventArg[0] == "ActEpDesc" and len(eventArg) >= 3: if "00" == eventArg[2]: devKey = GetKey(eventArg[1]) if devKey != None: database.SetDeviceItem(devKey, "endPoints", eventArg[3]) # Note first endpoint elif eventArg[0] == "SimpleDesc" and len(eventArg) >= 3: if "00" == eventArg[2]: globalDevKey = GetKey( eventArg[1] ) # Is multi-line response, so expect rest of response and use this global index until it's all finished elif "82" == eventArg[2]: # 82 == Invalid endpoint devKey = GetKey(eventArg[1]) events.Issue(events.ids.RXERROR, int( eventArg[2], 16)) # Tell system that we're aborting this command elif eventArg[0] == "InCluster" and len(eventArg) >= 2: if globalDevKey != None: database.SetDeviceItem( globalDevKey, "inClusters", str(eventArg[1:]) ) # Store whole list from arg[1] to arg[n] elif eventArg[0] == "OutCluster" and len(eventArg) >= 2: if globalDevKey != None: NoteMsgDetails( globalDevKey, eventArg ) # Must do this so that we can remove RSSI and LQI if they're there, to avoid these values being interpreted as clusters database.SetDeviceItem( globalDevKey, "outClusters", str(eventArg[1:]) ) # Store whole list from arg[1] to arg[n] globalDevKey = None # We've finished with this global for now elif eventArg[0] == "RESPATTR" and len(eventArg) >= 7: devKey = GetKey(eventArg[1]) if devKey != None: NoteMsgDetails(devKey, eventArg) if len( eventArg ) >= 7: # Check for number of args after possibly removing RSSI and LQI ep = eventArg[2] clusterId = eventArg[3] attrId = eventArg[4] if "00" == eventArg[5]: attrVal = eventArg[6] SetAttrVal(devKey, clusterId, attrId, attrVal) else: SetAttrVal(devKey, clusterId, attrId, "Failed (error " + eventArg[5] + ")") # So that we don't keep asking elif eventArg[0] == "RESPMATTR" and len(eventArg) >= 8: devKey = GetKey(eventArg[1]) if devKey != None: NoteMsgDetails(devKey, eventArg) if len( eventArg ) >= 8: # Check for number of args after possibly removing RSSI and LQI ep = eventArg[2] mfgId = eventArg[3] clusterId = eventArg[4] attrId = eventArg[5] if "00" == eventArg[6]: attrVal = eventArg[7] SetAttrVal(devKey, clusterId, attrId, attrVal) elif eventArg[0] == "REPORTATTR" and len(eventArg) >= 7: devKey = GetKey(eventArg[1]) if devKey != None: ep = eventArg[2] clusterId = eventArg[3] attrId = eventArg[4] attrType = eventArg[5] attrVal = eventArg[6] if clusterId == zcl.Cluster.MultistateInput and attrId == zcl.Attribute.PresentValue: args = [attrVal, eventArg[1]] events.Issue(events.ids.MULTISTATE, args) return # Ignore reports on Basic cluster (eg lumi.sensor_cube when it joins will send this) #if clusterId == zcl.Cluster.Basic: # return # Ignore reports on Basic cluster (eg lumi.sensor_cube when it joins will send this) NoteMsgDetails(devKey, eventArg) EnsureReporting( devKey, clusterId, attrId, attrVal ) # Make sure reports are happening at the correct frequency and update device if not SetAttrVal(devKey, clusterId, attrId, attrVal) NoteReporting(devKey, clusterId, attrId) else: # Unknown device, so assume it's been deleted from our database telegesis.Leave( eventArg[1] ) # Tell device to leave the network, since we don't know anything about it elif eventArg[0] == "Bind" and len( eventArg) >= 2: # Binding Response from device devKey = GetKey(eventArg[1]) if devKey != None: if pendingBinding[devKey]: binding = eval( database.GetDeviceItem(devKey, "binding", "[]")) if pendingBinding[ devKey] not in binding: # Only put it in once, even if we get multiple responses binding.append(pendingBinding[devKey]) database.SetDeviceItem(devKey, "binding", str(binding)) pendingBinding[devKey] = None elif eventArg[0] == "CFGRPTRSP" and len( eventArg) >= 5: # Configure Report Response from device devKey = GetKey(eventArg[1]) status = eventArg[4] if devKey != None and status == "00": clusterId = eventArg[3] attrId = pendingRptAttrId # Need to remember this, since it doesn't appear in CFGRPTRSP NoteReporting(devKey, clusterId, attrId) pendingRptAttrId = None # Ready for the next report elif eventArg[0] == "CWSCHEDULE": heating.ParseCWShedule(eventArg) elif eventArg[0] == "DFTREP": devKey = GetKey(eventArg[1]) NoteMsgDetails(devKey, eventArg) #else: # Unrecognised message, but we still want to extract OOB info # if len(eventArg) >= 2: # devKey = GetKey(eventArg[1]) # Assume this is sensible # if devKey != None: # NoteMsgDetails(devKey, eventArg) #if eventId == events.ids.BUTTON: # devKey = GetKey(eventArg[1]) # Lookup device from network address in eventArg[1] # NoteMsgDetails(devKey, eventArg) if eventId == events.ids.RXERROR: globalDevKey = None # We've finished with this global if we get an error if eventId == events.ids.SECONDS: for devKey in devDict: # Go through devDict, pulling out each entry if devDict[devKey] >= 0: # Make sure device hasn't been deleted if IsListening(devKey): # True if FFD, ZED or Polling devIndex = GetIndexFromKey(devKey) if expRsp[ devIndex] == None: # We don't have a message in flight if queue.IsEmpty(devKey): cmdRsp = Check(devKey) if cmdRsp: queue.EnqueueCmd( devKey, cmdRsp ) # Queue up anything we ought to know cmdRsp = queue.DequeueCmd( devKey) # Pull first item from queue if cmdRsp != None: log.debug("Sending " + str(cmdRsp)) expRsp[devIndex] = cmdRsp[1] # Note response expRspTimeoutS[ devIndex] = 2 # If we've not heard back after 2 seconds, it's probably got lost, so try again telegesis.TxCmd(cmdRsp[0]) # Send command directly else: # We're expecting a response, so time it out expRspTimeoutS[ devIndex] = expRspTimeoutS[devIndex] - eventArg if expRspTimeoutS[devIndex] <= 0: expRsp[devIndex] = None if pendingBinding[ devKey]: # Make sure we timeout pendingBinding pendingBindingTimeoutS[ devKey] = pendingBindingTimeoutS[devKey] - eventArg if pendingBindingTimeoutS[devKey] <= 0: pendingBinding[devKey] = None offAt = GetTempVal(devKey, "SwitchOff@") if offAt: if datetime.now() >= offAt: DelTempVal(devKey, "SwitchOff@") devcmds.SwitchOff(devKey) fadeDownAt = GetTempVal(devKey, "FadeDown@") if fadeDownAt: if datetime.now() >= fadeDownAt: DelTempVal(devKey, "FadeDown@") devcmds.Dim(devKey, 0) devcmds.SwitchOff(devKey) # Switch off after dim command pirOffAt = GetTempVal(devKey, "PirInactive@") if pirOffAt: if datetime.now() >= pirOffAt: DelTempVal(devKey, "PirInactive@") newState = "inactive" database.NewEvent(devKey, newState) Rule(devKey, newState)
def SetAttrVal(devKey, clstrId, attrId, value): global msp_ota if clstrId == zcl.Cluster.PowerConfig and attrId == zcl.Attribute.Batt_Percentage: SetTempVal(devKey, "GetNextBatteryAfter", datetime.now() + timedelta(seconds=86400)) # Ask for battery every day if value != "FF": try: varVal = int( int(value, 16) / 2 ) # Arrives in 0.5% increments, but drop fractional component except ValueError: varVal = None if varVal != None: log.debug("Battery is " + str(varVal) + "%. Get next reading at " + str(GetTempVal(devKey, "GetNextBatteryAfter"))) database.LogItem(devKey, "BatteryPercentage", varVal) # For web page lowBatt = int(config.Get("lowBattery", "5")) if varVal < lowBatt: devName = database.GetDeviceItem(devKey, "userName") synopsis.problem( devName + "_batt", devName + " low battery (" + str(varVal) + "%)") if clstrId == zcl.Cluster.Temperature and attrId == zcl.Attribute.Celsius: if value != "FF9C" and value != "8000": # Don't know where this value (of -100) comes from, but seems to mean "Illegal temp", although it should be -1'C try: varVal = int(value, 16) / 100 # Arrives in 0.01'C increments database.LogItem(devKey, "TemperatureCelsius", varVal) # For web page except ValueError: log.debug("Bad temperature of " + value) if clstrId == zcl.Cluster.OnOff and attrId == zcl.Attribute.OnOffState: if isnumeric(value, 16): oldState = database.GetLatestLoggedValue(devKey, "State") if int(value, 16) == 0: newState = "SwitchOff" else: newState = "SwitchOn" if oldState != newState: database.UpdateLoggedItem( devKey, "State", newState) # So that we can access it from the rules later database.NewEvent(devKey, newState) Rule(devKey, newState) expectedState = GetTempVal(devKey, "ExpectOnOff") if expectedState != None: if newState != expectedState: if expectedState == "SwitchOn": devcmds.SwitchOn(devKey) # Re-issue command else: # Assume SwitchOff devcmds.SwitchOff(devKey) # Re-issue command else: # We got the expected result DelTempVal(devKey, "ExpectOnOff") if clstrId == zcl.Cluster.Time and attrId == zcl.Attribute.LocalTime: if isnumeric(value, 16): varVal = int(value, 16) # Arrives in Watts, so store it in the same way log.debug("Raw time:" + str(varVal)) timeStr = iottime.FromZigbee(varVal) log.debug("Human time:" + timeStr) database.UpdateLoggedItem(devKey, "Time", timeStr) # Just store latest time string if clstrId == zcl.Cluster.SimpleMetering and attrId == zcl.Attribute.InstantaneousDemand: if isnumeric(value, 16): varVal = int(value, 16) # Arrives in Watts, so store it in the same way inClstr = database.GetDeviceItem( devKey, "inClusters" ) # Assume we have a list of clusters if we get this far if zcl.Cluster.OnOff not in inClstr: # Thus device is powerclamp (has simplemetering but no OnOff) database.UpdateLoggedItem( devKey, "State", str(varVal) + "W" ) # So that we can access it from the rules later, or show it on the web database.UpdateLoggedItem(devKey, "PowerReadingW", varVal) # Just store latest reading if clstrId == zcl.Cluster.SimpleMetering and attrId == zcl.Attribute.CurrentSummationDelivered: if isnumeric(value, 16): varVal = int( value, 16 ) # Arrives in accumulated WattHours, so store it in the same way database.LogItem(devKey, "EnergyConsumedWh", varVal) if clstrId == zcl.Cluster.IAS_Zone and attrId == zcl.Attribute.Zone_Type: database.SetDeviceItem(devKey, "iasZoneType", value) if clstrId == zcl.Cluster.Basic: if attrId == zcl.Attribute.Model_Name: database.SetDeviceItem(devKey, "modelName", value) if attrId == zcl.Attribute.Manuf_Name: database.SetDeviceItem(devKey, "manufName", value) if clstrId == zcl.Cluster.OTA or clstrId == msp_ota: if attrId == zcl.Attribute.firmwareVersion: database.SetDeviceItem(devKey, "firmwareVersion", value) if clstrId == zcl.Cluster.PollCtrl: if attrId == zcl.Attribute.LongPollIntervalQs: varVal = str(float(int(value, 16) / 4)) # Value arrives in units of quarter seconds database.SetDeviceItem( devKey, "longPollInterval", varVal ) # For web page and also to see whether to wait for CheckIn or just send messages (if <6 secs) if clstrId == zcl.Cluster.Thermostat: if attrId == zcl.Attribute.LocalTemp: if isnumeric(value, 16): varVal = int(value, 16) / 100 # Arrives in 0.01'C increments database.LogItem(devKey, "SourceCelsius", varVal) # For web page src = varVal tgt = database.GetLatestLoggedValue(devKey, "TargetCelsius") database.UpdateLoggedItem( devKey, "State", "source " + str(src) + "'C/target " + str(tgt) + "'C") # So that we can show it on the web if attrId == zcl.Attribute.OccupiedHeatingSetPoint: if isnumeric(value, 16): varVal = int(value, 16) / 100 # Arrives in 0.01'C increments database.LogItem(devKey, "TargetCelsius", varVal) # For web page tgt = varVal src = database.GetLatestLoggedValue(devKey, "SourceCelsius") database.UpdateLoggedItem( devKey, "State", "source " + str(src) + "'C/target " + str(tgt) + "'C") # So that we can show it on the web if clstrId == zcl.Cluster.Time: if attrId == zcl.Attribute.Time: if isnumeric(value, 16): varVal = int(value, 16) # Arrives in seconds since 1st Jan 2000 timeStamp = iottime.FromZigbee(varVal) database.LogItem(devKey, "time", str(timeStamp)) # For web page
def Check(devKey): global pendingBinding, msp_ota if devKey == 0: return # We don't need anything from the hub nwkId = database.GetDeviceItem(devKey, "nwkId") if None == nwkId: return # Make sure it's a real device before continuing (it may have just been deleted) ep = database.GetDeviceItem(devKey, "endPoints") if None == ep: return (["AT+ACTEPDESC:" + nwkId + "," + nwkId, "ActEpDesc"]) eui = database.GetDeviceItem(devKey, "eui64") if None == eui: return (["AT+EUIREQ:" + nwkId + "," + nwkId, "AddrResp"]) inClstr = database.GetDeviceItem( devKey, "inClusters", "[]") # Assume we have a list of clusters if we get this far if "[]" == inClstr: return ([ "AT+SIMPLEDESC:" + nwkId + "," + nwkId + "," + ep, "OutCluster" ]) outClstr = database.GetDeviceItem(devKey, "outClusters", "[]") binding = database.GetDeviceItem(devKey, "binding" "[]") if str(pendingBinding[devKey] ) == "": # Only try to add one binding per device at once if zcl.Cluster.PollCtrl in inClstr and zcl.Cluster.PollCtrl not in binding: return SetBinding( devKey, zcl.Cluster.PollCtrl, "01") # 01 is our endpoint we want CHECKIN messages to come to if zcl.Cluster.OnOff in outClstr and zcl.Cluster.OnOff not in binding: # If device sends OnOff commands (eg a Button) return SetBinding( devKey, zcl.Cluster.OnOff, "0A" ) # 0A is our endpoint we want messages to come to (so that we get TOGGLE, ON and OFF commands) if zcl.Cluster.Temperature in inClstr and zcl.Cluster.Temperature not in binding: return SetBinding( devKey, zcl.Cluster.Temperature, "01" ) # 01 is our endpoint we want Temperature reports to come to if zcl.Cluster.SimpleMetering in inClstr and zcl.Cluster.SimpleMetering not in binding: return SetBinding( devKey, zcl.Cluster.SimpleMetering, "01" ) # 01 is our endpoint we want SimpleMetering messages to come to if zcl.Cluster.Thermostat in inClstr and zcl.Cluster.Thermostat not in binding: return SetBinding( devKey, zcl.Cluster.Thermostat, "01" ) # 01 is our endpoint we want Thermostat messages to come to if zcl.Cluster.IAS_Zone in inClstr: if None == database.GetDeviceItem(devKey, "iasZoneType"): return telegesis.ReadAttr( nwkId, ep, zcl.Cluster.IAS_Zone, zcl.Attribute.Zone_Type ) # Get IAS device type (PIR or contact, etc.) if zcl.Cluster.Basic in inClstr: if None == database.GetDeviceItem(devKey, "modelName"): return telegesis.ReadAttr( nwkId, ep, zcl.Cluster.Basic, zcl.Attribute.Model_Name) # Get Basic's Device Name if None == database.GetDeviceItem(devKey, "manufName"): return telegesis.ReadAttr( nwkId, ep, zcl.Cluster.Basic, zcl.Attribute.Manuf_Name) # Get Basic's Manufacturer Name if zcl.Cluster.PowerConfig in inClstr and "SED" == database.GetDeviceItem( devKey, "devType"): checkBatt = GetTempVal(devKey, "GetNextBatteryAfter") if checkBatt != None: if datetime.now() > checkBatt: log.debug("Now = " + str(datetime.now()) + " and checkBatt = " + str(checkBatt)) return telegesis.ReadAttr( nwkId, ep, zcl.Cluster.PowerConfig, zcl.Attribute.Batt_Percentage) # Get Battery percentage if zcl.Cluster.PollCtrl in inClstr: if None == database.GetDeviceItem(devKey, "longPollInterval"): return telegesis.ReadAttr( nwkId, ep, zcl.Cluster.PollCtrl, zcl.Attribute. LongPollIntervalQs) # Get Poll Control's Long poll interval if zcl.Cluster.OTA in outClstr: if None == database.GetDeviceItem(devKey, "firmwareVersion"): return ("AT+READCATR:" + nwkId + "," + ep + ",0," + zcl.Cluster.OTA + "," + zcl.Attribute.firmwareVersion, "RESPATTR" ) # Get OTA's Version number as a string of hex digits if msp_ota != None and msp_ota in outClstr: if None == database.GetDeviceItem(devKey, "firmwareVersion"): return ("AT+READMCATR:" + nwkId + "," + ep + ",0," + config.Get(mfgId) + "," + msp_ota + "," + zcl.Attribute.firmwareVersion, "RESPMATTR" ) # Get OTA's Version number as a string of hex digits reporting = database.GetDeviceItem(devKey, "reporting", "[]") if zcl.Cluster.PowerConfig in binding and "SED" == database.GetDeviceItem( devKey, "devType"): atCmd = CheckReporting( devKey, reporting, "batteryReporting", zcl.Cluster.PowerConfig, zcl.Attribute.Batt_Percentage, zcl.AttributeTypes.Uint8, "43200,43200,2" ) # Default temperature reporting is "Every 12 hours" if atCmd != None: return atCmd if zcl.Cluster.Temperature in binding: atCmd = CheckReporting( devKey, reporting, "temperatureReporting", zcl.Cluster.Temperature, zcl.Attribute.Celsius, zcl.AttributeTypes.Uint16, "300,3600,100" ) # Default temperature reporting is "between 5 mins and 1 hr, or +/- 1.00'C" if atCmd != None: return atCmd if zcl.Cluster.SimpleMetering in binding: atCmd = CheckReporting( devKey, reporting, "powerReporting", zcl.Cluster.SimpleMetering, zcl.Attribute.InstantaneousDemand, zcl.AttributeTypes.Sint24, "-1,-1,10" ) # Default power reporting is "between 5 seconds and 15 minutes, or +/- 10W" if atCmd != None: return atCmd atCmd = CheckReporting( devKey, reporting, "energyConsumedReporting", zcl.Cluster.SimpleMetering, zcl.Attribute.CurrentSummationDelivered, zcl.AttributeTypes.Uint48, "-1,-1,100" ) # Default energy consumed reporting is "between 1 minute and 15 minutes, or +100Wh" if atCmd != None: return atCmd atCmd = CheckReporting( devKey, reporting, "energyGeneratedReporting", zcl.Cluster.SimpleMetering, zcl.Attribute.CurrentSummationReceived, zcl.AttributeTypes.Uint48, "-1,-1,0" ) # Default energy generated reporting is "never" (-1 as max) if atCmd != None: return atCmd if zcl.Cluster.Thermostat in binding: atCmd = CheckReporting( devKey, reporting, "targetTempReporting", zcl.Cluster.Thermostat, zcl.Attribute.OccupiedHeatingSetPoint, zcl.AttributeTypes.Sint16, "60,900,100" ) # Default target temperature reporting is "between 1 minute and 15 minutes, or +/-1.00'C" if atCmd != None: return atCmd if GetTempVal(devKey, "JustSentOnOff"): DelTempVal(devKey, "JustSentOnOff") return telegesis.ReadAttr( nwkId, ep, zcl.Cluster.OnOff, zcl.Attribute.OnOffState) # Get OnOff state after sending toggle return None