Tweet-a-watt - hur man gör en kvittrande kraftmätare... (17 / 19 steg)
Steg 17: Design - diagram
Data är stor, men visuella effekter är bättre. I detta steg ska vi manipulerar våra lagrade historia så att vi kan göra riktigt fina grafer!
Första vi börjar med att göra våra sensorer heter, så att det är lättare för oss att hålla reda på vilket är vad. Sedan kommer vi att titta på våra graf alternativ och dataformat. Slutligen vi ska formatera om våra data för att göra det redo för grafritande
Konfigurera sensorn namnen
Dess inte roligt att ha data markeras som "sensor #1" så jag la en 'config' sida där app engine koden ser ut på vilka sensor siffror har skickat data till databasen och sedan kan du namnge dem. Naturligtvis måste du ha sensorn på och skicka data - först - innan detta kommer att fungera
Skärmen konfigurera ser ut ungefär som bilden nedan.
Denna kod använder GET när det borde verkligen använda POST. Jag är ganska gammal och inte gillar felsökning med POST so... yeah.
Undergrupp konfigurera (webapp. RequestHandler):
def get(self):
# gör användaren logga in om inget användarnamn levereras
om self.request.get('user'):
konto = användare. User(Self.Request.get('user'))
annat:
om inte users.get_current_user():
Self.Redirect(users.create_login_url(Self.Request.URI))
konto = users.get_current_user()
Self.Response.out.write ("< html >< kropp > uppsättningen upp din sensornode namn här: < p >')
# Hitta alla sensorer upp till #10
sensorset =]
för i i range(10):
c = db. GqlQuery ("Välj * från Powerusage där författaren =: 1 och sensornum =: 2", users.get_current_user(), i)
om c.get():
sensorset.append(i)
Self.Response.out.write ("< bildar åtgärd =" / config "metod ="Hämta">")
för sensor i sensorset:
namn = ""
currnamequery = db. GqlQuery ("Välj * från Sensorname där författaren =: 1 och sensornum =: 2", users.get_current_user(), sensor)
currName = currnamequery.get()
# först se om vi ställer det!
om self.request.get('sensornum'+str(sensor)):
namn = self.request.get('sensornum'+str(sensor))
om inte currname:
currName = Sensorname() # skapa en ny post
currName.sensornum = sensor
currName.author = users.get_current_user()
currName.sensorname = namn
currName.Put()
annat:
# vi inte ställer det så fetch nuvarande post
om currname:
namn = currname.sensorname
Self.Response.out.write ("Sensor #' + str(sensor) +': < input type ="text"name="sensornum'+str(sensor) + "" värde = "" + namn +"" >< / text >< p >')
Self.Response.out.write ("" "< div >< input type =" Skicka"värde ="Byt namn">< / div >
< / form >
< / body >
< / html > "" ")
Nu kan vi ha mer användbar data i historia soptipp
Nu kan vi se att Phil mestadels är att skylla för våra elräkningen!
Google Visualizer
Så vi har data och vi skulle vilja se våra power användning historia. Graphing data är en hel del arbete, och jag är lat. Så jag leta på nätet och tycker att Google - också - har en visualisering API! Detta innebär att jag inte behöver skriva ett gäng grafiska kod, och kan bara koppla in i deras system. Söt!
OK kolla galleriet av tillgängliga visualiseringar, är jag förtjust i detta en, Kommenterad tidslinje
Notera hur du lätt kan se grafer, rulla runt, zooma in och ut och varje handling är märkt. Perfekt för plottning ström data!
Dataformatering
Theres några begränsningar med hur vi får data till visualisering api och våra bästa alternativet är JSon data. Såvitt jag kan säga, är JSON vad som hände när alla sa "wow, XML är på riktigt skrymmande och slösaktiga". Hur som helst, theres som 4 lager av ram och tolkande data instruktionerna och till slut fanns det ett ganska lätt att använda bibliotek Skrivet av Google visualiseringar team som jag vill "bara gör det" med ett enda samtal genom att sätta data i en python "dictionary" i ett visst format.
Släpper igenom koden i sektioner, eftersom funktionen är ganska lång
klass JSONout(webapp. RequestHandler):
def get(self):
# gör användaren logga in om inget användarnamn levereras
om self.request.get('user'):
konto = användare. User(Self.Request.get('user'))
annat:
om inte users.get_current_user():
Self.Redirect(users.create_login_url(Self.Request.URI))
konto = users.get_current_user()
# antar vi vill 24 timmar av data
historytimebegin = 24
om self.request.get('bhours'):
historytimebegin = int(self.request.get('bhours'))
# antar vi vill data start från 0 timmar sedan
historytimeend = 0
om self.request.get('ehours'):
historytimeend = int(self.request.get('ehours'))
# dataformat för JSON lycka
DataStore =]
columnnames = ["datum"]
ColumnSet = set(columnnames)
Beskrivning = {"datum": ("datetime", "Datum")}
# namn på varje sensor, om konfigurerat
sensornames = [ingen] * 10
Först får upp vi vi kommer att titta upp data för användaren. Sedan har vi två variabler för att fastställa mängden data till hugg. En är "ehours" (slutet timmar) och den andra är "bhours". Så om du ville de senaste 5 timmarna, bhours skulle vara 5 och ehours skulle vara 0. Om du ville ha 5 timmar från en dag sedan, bhours 29 och ehours skulle vara 24. DataStore är där vi kommer corall alla data. columnnames och beskrivning är "namnen" på varje kolumn. Vi har alltid en datumkolumn, sedan en annan kolumn för varje sensor ström. Vi har även en separat array cachelagra särskilda sensor namnen.
till nästa avsnitt! Här är där vi faktiskt ta data från databasen. Nu app engine har detta irriterande begränsningar, kan du bara få 1000 poäng för data på en gång så vad jag gör är att gå igenom den 12 timmar i taget. Den slutliga datastore har alla poäng men det är lättare på databasen, antar jag. En sak som är förvirrande kanske är varje kolumn har ett namn och en beskrivning. Namnet är kort, säga "watts3" för sensor #3, men beskrivningen kan vara "Limor's workbench". Jag minns inte ens skriva denna kod så kanske du kan lista ut på egen hand?
# vi inte kan ta mer än 1000 datapoints, tack vare gratis-app-engine begränsning
# Det är ca 3 sensorer värd på en dag
# så vi kommer att begränsa till bara greppa 12 timmar av data i en tid, omkring 7 sensorer värt
medan (historytimebegin > historytimeend):
om (historytimebegin - historytimeend) > 12:
timebegin = datetime.timedelta (hours = - historytimebegin)
timeEnd = datetime.timedelta (hours =-(historytimebegin-12))
historytimebegin-= 12
annat:
timebegin = datetime.timedelta (hours = - historytimebegin)
historytimebegin = 0
timeEnd = datetime.timedelta (hours = - historytimeend)
# ta alla sensordata för denna tid bit
powerusages = db. GqlQuery ("Välj * från Powerusage där datum >: 1 och datum <: 2 och författare =: 3 ORDER BY date", datetime.datetime.now () + timebegin, datetime.datetime.now () + timeend, hänsyn)
# sortera dem i rätt format och lägga till sensor namn från att DB om inte klar än
för powerused i powerusages:
Coln = "watt" + str(powerused.sensornum)
Entry = {"datum": powerused.date.replace(tzinfo=utc).astimezone(est), coln: powerused.watt}
om inte (coln i columnset):
columnnames.append(Coln)
ColumnSet = set(columnnames)
# hitta sensor namnet, om vi kan
om (len(sensornames) < powerused.sensornum) eller (inte sensornames[powerused.sensornum]):
currnamequery = db. GqlQuery ("Välj * från Sensorname där författaren =: 1 och sensornum =: 2", konto, powerused.sensornum)
namn = currnamequery.get()
om inte namn:
sensornames[powerused.sensornum] = "sensor #"+str(powerused.sensornum)
annat:
sensornames[powerused.sensornum] = name.sensorname
Beskrivning [coln] = ("nummer", sensornames[powerused.sensornum])
#self.response.out.write(sensornames)
# lägga till en post i taget
DataStore.append(Entry)
Slutligen i slutet av alla looping, vi anropa funktionen magic som förvandlar ordboken till JSON, Linda in den i rätt Google visualisering paketet, sedan spotta ut!
# OK alla data är redo att gå, skriva ut den i JSON-format!
data_table = gviz_api. DataTable(description)
data_table. LoadData(datastore)
Self.Response.headers ["Content-Type"] = "text/plain"
Self.Response.out.write (data_table. ToJSonResponse(columns_order=(columnnames),
order_by = "datum"))
Om du var att besöka http://wattcher.appspot.com/visquery.json?user=adawattz skulle det ut ungefär så här:
google.visualization.Query.setResponse({'version':'0.5', 'reqId':'0', 'status':'OK', 'table': {cols: [{id:'date',label:'Date',type:'datetime'},{id:'watts1',label:'Limor',type:'number'},{id:'watts5',label:'Workbench',type:'number'},{id:'watts2',label:'Adafruit',type:'number'},{id:'watts4',label:'Phil2',type:'number'}],rows: [{c:[{v:new Date(2009,1,25,21,20,2)},{v:64.8332291619},,,{v:null}]},{c:[{v:new Date(2009,1,25,21,20,3)},,{v:230.122099757},,{v:null}]},{c:[{v:new Date(2009,1,25,21,20,3)} ,,, {v: 65.4923925044}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,20,4)},,, {v: 48.6947643311}]}, {c: [{v: nya Date(2009,1,25,21,25,3)},, {v: 228.409810208}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,25,3)}, {v: 67.3574917331},,, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,25,3)},,, {v: 66.0046383897}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,25,4)},,, {v: 47.3892235642}]}, {c: [{v: nya Date(2009,1,25,21,30,2)}, {v: 84.9379517795}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,30,3)},,, {v: 99.7553490071}]} , {c: [{v: nya Date(2009,1,25,21,30,5)},, {v: 229.73642288}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,30,6)},,, {v: 66.6556291818}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,35,2)},,, {v: 67.3146052998}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,35,3)}, {v: 96.2322216676}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,35,3)},, {v: 226.678267688}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,35,4)},,, {v: 158.428422765}]}, {c: [{v: nya Date(2009,1,25,21,40,3)},, {v: 232.644574879}, {v: null}]} , {c: [{v: nya Date(2009,1,25,21,40,4)},,, {v: 153.666193493}]}, {c: [{v: nya Date(2009,1,25,21,40,6)},,, {v: 66.7874343225}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,40,12)}, {v: 95.0019590395},,, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,40,21)}, {v: 95.0144043571}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,40,23)},,, {v: 66.8060307611}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,45,2)},,, {v: 66.9814723201}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,45,3)},, {v: 226.036818816}, {v: null}]} , {c: [{v: nya Date(2009,1,25,21,45,3)}, {v: 99.2775581827},,, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,45,4)},,, {v: 154.261889366}]}, {c: [{v: nya Date(2009,1,25,21,50,4)}, {v: 102.104642018},,, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,50,4)},,, {v: 155.441084531}]}, {c: [{v: nya Date(2009,1,25,21,50,5)},,, {v: 67.0087146687}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,50,5)},, {v: 230.678636915}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,55,3)}, {v: 103.493297176}, {v: null}]}, {c: [{v : nya Date(2009,1,25,21,55,3)},,, {v: 151.309223916}]}, {c: [{v: nya Date(2009,1,25,21,55,4)},,, {v: 66.9174858741}, {v: null}]}, {c: [{v: nya Date(2009,1,25,21,55,4)},, {v: 227.765325835}, {v: null}]}, {c: [{v: nya Date(2009,1,25,22,0,3)},,, {v: 67.0004310254}, {v: null}]}, {c: [{v: nya Date(2009,1,25,22,0,3)},,, {v: 150.389989112}]}, {c: [{v: nya Date(2009,1,25,22,0,3)},, {v: 230.892049553}, {v: null}]}, {c: [{v: nya Date(2009,1,25,22,0,4)}, {v: 92.2432771363}, {v: null}]}, {c: [{v: nya Date(2009,1,25,22,15,3)} {v: 97.5910440774}, {v: null}]}, {c: [{v: nya Date(2009,1,25,22,15,3)},,, {v: 143.722595861}]}, {c: [{v: nya Date(2009,1,25,22,15,4)},,, {v: 64.4898008851}, {v: null}]}, {c: [{v: ny Date(2009,1,25,22,15,4)},,{v:222.357617868},,{v:null}]}]}});
Hur som helst, kan du ganska se data, Observera också dess faktiskt ett funktionsanrop, det här är riktigt kinky!
Nu gå till Google visualiseringar lekplats och fyll i Webbadressen i sandlådan
Och du kan se visualisering sig pop ut! (detta är bara en skärm skott så gå göra det väntar om du vill röra runt)
OK gå röra runt, lägga till och ändra bhours och ehours
Inslagning upp visualisering
OK är vi nästan klara. Nu behöver vi bara i princip ta koden från sandlådan och göra den en delsida i vår app motor... som så:
Undergrupp visualisera (webapp. RequestHandler):
def get(self):
# gör användaren logga in om inget användarnamn levereras
om self.request.get('user'):
konto = användare. User(Self.Request.get('user'))
annat:
om inte users.get_current_user():
Self.Redirect(users.create_login_url(Self.Request.URI))
konto = users.get_current_user()
historytimebegin = 24 # ta 24 timmar
om self.request.get('bhours'):
historytimebegin = int(self.request.get('bhours'))
historytimeend = 0 # antar 0 timmar sedan
om self.request.get('ehours'):
historytimeend = int(self.request.get('ehours'))
# få den första delen, headers, ute
() Self.Response.out.Write
<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict / / EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" >
< html xmlns = "http://www.w3.org/1999/xhtml" >
< head >
< meta http-equiv = "content-type" content = "text/html; charset = utf-8 "/ >
< title > Google Visualization API prov < / title >
< script type = "text/javascript" src = "http://www.google.com/jsapi" >< / script >
< script type = "text/javascript" >
Google.load ("visualisering", "1", {paket: ["annotatedtimeline"]});
funktion drawVisualizations() {
)
# skapa våra visualisering
Self.Response.out.write (nya google.visualization.Query ("http://wattcher.appspot.com/visquery.json?user=+
Account.email() +& bhours =+ str(historytimebegin) +") .send (
function(Response) {
nya google.visualization.AnnotatedTimeLine)
document.getElementById("visualization")).
draw(Response.getDataTable(), {"displayAnnotations": sant});
});
)
Self.Response.out.write (}
google.setOnLoadCallback(drawVisualizations);
< / script >
< / head >
< body style = "font-family: Arial, gränsen: 0 ingen;" >
< div id = "visualisering" style = "bredd: 800px; höjd: 250px; " >< / div >
< / body >
< / html >)
Den första delen är ganska rakt framåt, få användar- eller logga in. Sedan kommer vi att anta att användaren vill 1 sista dagen data, så som bhours och ehours. Sedan skriva vi bokstavligen bara ut den kod vi kopierat från Googles visualisering sandlåda, gjort!
Viz Viz Viz
Det enda jag inte kunde lista ut är hur man får 3 visualiseringar händer på en gång (sista timme, dag och vecka) med ovanstående kod. Det bröt bara kinda. Så för vyn trippel hade jag gå använda iframes :(
klass VisualizeAll(webapp. RequestHandler):
def get(self):
# gör användaren logga in om inget användarnamn levereras
om self.request.get('user'):
konto = användare. User(Self.Request.get('user'))
annat:
om inte users.get_current_user():
Self.Redirect(users.create_login_url(Self.Request.URI))
konto = users.get_current_user()
() Self.Response.out.Write
< h2 > Power användning under den senaste timmen: < / h2 >
< iframe src = "graf? användare = adawattz frameborder ="0"width ="100% "höjd ="300px">
< p > webbläsaren inte stöder iframes. < /p >
< / iframe >
< h2 > Power användning under den sista dagen: < / h2 >
< iframe src = "graf? användare = adawattz frameborder ="0"width ="100% "höjd ="300px">
< p > webbläsaren inte stöder iframes. < /p >
< / iframe >
< h2 > strömförbrukning under den senaste veckan: < / h2 >
< iframe src = "graf? användare = adawattz frameborder ="0"width ="300% "höjd ="500px">
< p > webbläsaren inte stöder iframes. < /p >
< / iframe >
)
Hur som helst, det fungerar alldeles utmärkt.
Timecodes!
Det sista som inte kommer att ses här är hur jag fick datum och tider att vara EST istället för UTC. Såvitt jag kan säga, dess sort av trasiga och mystiska. Kontrollera koden om du vill räkna ut.