Wednesday, July 20, 2016

Some very short code snippets: Nightscout and the Windows Deskop.

In this post I will provide a few code snippets that could be useful if you want to monitor SGV values stored in a MLAB database. In this case, the Windows desktop.

Just a few words of warnings

  • you'll need a mlab database with current sgv values (either through direct upload or through a full Nighscout install.
  • you'll need a minimal understanding of what a program is, how to install Python on your computer and how to edit source code with your own parameters.
  • the examples given below are absolutely minimal and provide no error checking. I just rewrote them from a bigger, more complex program I wrote and use.
  • I won't have the time to answer python questions. :)
What we need is simply
  • get value(s) from the database
  • create a bitmap we want as a background with the SGV data on it
  • force windows to update our background
And we'll get something like this 



How to connect to mongo and get one recent result


# import the required package to connect to mongodb
from pymongo import MongoClient
# not really needed, but will help if you don't want to display the full length date
import dateutil.parser as dup
# will allow us to get the time
import time
# database connection setup - your own user, password, port and db should be here
# there are other ways to connect to the db, this is just one example
uri = "mongodb://youruser:yourpassword@ds099999.mlab.com:99999/yourdb?authMechanism=SCRAM-SHA-1"
# open the connection to the database
client = MongoClient(uri)
# set the database we want to use (must be your db name)
db = client['yourdb']
# set the collection we need (must be your collection name)
entries = db['entries']
# lets set the time to now - 300 seconds (we want to check if we have a value in the last 5 mins)
# python uses time stamps in seconds, javascript uses time stamps in miliseconds, hence the *1000
starttime = (time.time()-300)*1000
# we query the db for a value after now - 300 seconds
cursor = db.entries.find({"date": {"$gt": starttime}})
# check that we have a result, we don't always
if cursor.count() > 0:
    for i in cursor:
        # check if it is a sgv value, it could be a calibration record, or anything else
        # we could also query a value satisfying both conditions sgv present and time greater 
        # than now minus 300 sec but the query above would be less obvious in a tutorial
        if 'sgv' in i:
            # print the content of the record
            print('SGV', i['sgv'], 'at', dup.parse(i['dateString']).strftime('%H:%M'))
        else:
            print('No SGV data found')
else:
    print("No result found")

How to create bitmaps for backgrounds and activate them


# required for image manipulation
from PIL import Image, ImageDraw, ImageFont
# required for API calls
import ctypes
# my screen resolution is 3440 pixels wide and 1440 pixels high
# it could be different from yours
w = 3440
h = 1440
# these are the coordinates where I want to plot my text
xt = 2500
yt = 1300

# these are for the windows API command for "set wallpaper"
SPI_SETDESKWALLPAPER = 20  
SPI_FORCEUPDATE = 2

# this is my current background, I want to keep a clean copy
bgsource = 'c:/temp/iceflow.jpg'
# this is the background I am planning to display
bgdest = 'c:/temp/mybg.jpg'
# the windows API required string style
APIpath = r'c:\temp\mybg.jpg'
# this is the font I want to use, make sure you download it or 
# use any other font of your computer
font = 'c:/temp/FreeMono.ttf'
fsize = 100
imagemode = 'RGBA'

# I could also generate my own background
# background = bytes([81, 92, 107, 255]*w*h)
# base = Image.frombytes(imagemode, (w, h), background)

# but we use the image above image
base = Image.open(bgsource).convert(imagemode)

# make a blank image for the text, initialized to transparent text color
# we will superpose that image on our normal background
# it works like layers in Photoshop
txt = Image.new(imagemode, base.size, (255,255,255,0))
# get a font
fnt = ImageFont.truetype(font, fsize)
# get a drawing context to the layer we just created
d = ImageDraw.Draw(txt)
# draw some text, half opacity
d.text((xt,yt), "This is a test", font=fnt, fill=(255,255,255,128))
# mix the two images
# it's a merge of layers in Photoshop
out = Image.alpha_composite(base, txt)
# update the background bitmap
out.save(bgdest)
# windows API call on recent systems - this should force a background reset
# to our image
ctypescallresult = ctypes.windll.user32.SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, APIpath, SPI_FORCEUPDATE)

An additional test


The windows API can be capricious, depending on the version you have (64 or 32 bits), the simplest way to test it is to try setting up your background to an image already present on your computer

import ctypes

# see this link for more information
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947%28v=vs.85%29.aspx

SPI_SETDESKWALLPAPER = 20  # this is the windows command for set wallpaper
SPI_FORCEUPDATE = 2
# this is our background file
APIpath = r'c:\temp\iceflow.jpg'

ctypescallresult = ctypes.windll.user32.SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, APIpath, SPI_FORCEUPDATE)

The logic


Putting it all together, a minimal program would go like this

get a value, update the background, calculate when to request the next value and, from that point, loop indefinitely, creating the image and updating the background whenever a new value arrives (every five minutes).

That's it - dead easy. Left as an exercise for the reader. 



No comments:

Post a Comment