import RPi.GPIO as GPIO
import time, sys
import http.server
from http.server import BaseHTTPRequestHandler, HTTPServer
import socketserver
import _thread
import threading
import socket
from socketserver import ThreadingMixIn
import requests, json
import datetime
import array as arr
import statistics
import time
import json
import pprint
import math
import os
import ADS1x15
from timeit import Timer
from numpy import log as ln
from urllib.parse import unquote
data = {
'sahko_hinta_tanaan' : [0 for i in range(25)] ,
'sahko_hinta_huomenna' : [0 for i in range(25)] ,
'kulutus_spot' : [0],
'spot_kulutus_hinta' : [0],
'kumul_spot_kokonaishinta' : 0,
'kumul_kokonaiskulutus' : 0,
'kumul_pulssit' : 0,
'elspot_alv' : 1.193, #HOX Nordpoolin hinnat ovat verottomia. HOX ALV lasketaan verottomasta hinnasta ;)
'elspot_marginaali' : 0.01,
'piirturi_sahko' : [0],
'piirturi_pulssit' : 0,
'piirturi_count' : 1000, #Piirturi aloittaa seuraavasta tasa-vartista
'siirtoHintaYosahkolla' : 0,
'sahko' : {'pulsseja_laskettu' : 0,
'pulsseja_q' : 0,
'current_q' : 0,
'aloitusaika' : time.time(),
'pulssien_vali' : 10,
'pulssien_otanta' : 5,
'aikaleimat' : 0,
'realTimeMonitorLog' : [0],
'pulssit_yo' : 0,
'pulssit_paiva' : 0,
'lastTimestamp' : 0,
'pulsseja_per_kwh' : 10000 # yes, Mäntsälän Sähkö aivilon have 10 000 blinks per kWh ;)
},
'kv' : {'pulsseja_laskettu' : 0, #Täysin turha homma mutta en mene nyt sorkkimaan sitä
'vetta_kulunut' : 0,
'aloitusaika' : time.time(),
'pulssien_vali' : 0
},
'meta' : {'saikeitaKaynnissa' : 0,
'asiakkaitaPaikalla': 0,
'data_tumple_size' : 44, #Mikshän tuo on noin ?? Kysyisin itseltäin
'broadcastedBytes' : 0,
'nordpoolReqTimes' : 0,
'httpPyyntojaSaatu' : 0,
'allowNordpoolReq' : 1, #Vahditaan hieman ettei ala spämmiä Nordpooliin. Kerran jo pelastikin kun tein virheen koodiin
'paivamaaraTanaan' : datetime.datetime.today().strftime("%d-%m-%Y"),
'restartTimes' : 0,
'ajoaikaSec' : 0
},
'vesi' : {'taajuus' : 0,
'lastTimestamp' : 0,
'ulkokierto_count' : 0,
'kierron_lammitysteho' : 0,
'kierron_saasto' : 0,
'kierron_paluulampo' : 0,
'kylma_vesi_c' : 5, #kaivoveden lämpötila, lämmitystehon laskua varten TÄTÄ ARVOA EI VOI MUUTTAA JÄLKEENPÄIN, ei vaikuta takautuvasti lukemiin TODO: Hommaa mittari tohon
'kylma_vesi_taajuus' : 0,
'kylma_vesi_lastTimestamp' : 0,
'kylma_vesi_count' : 0,
'varaajan_kapasiteetti' : 99000, # 99000 pulssia mittarilta = 150 litraa,
'varaajan_havioteho' : 0.1, # kW
'varaajan_lammitysteho' : 1.92, # kW
'varaajan_status' : 99000, # Lähtötilanne = täynnä
'varaajan_pakotus' : False # True = pinout lähtö on silloin 0, avautuva kosketin ohjaa kontaktorin kelaa
},
'vesi_logi': { 'kylma_paivittain': [0], #HOX tätä ei striimata livenä, eritelty siksi veden alaisuudesta pois
'lammin_paivittain': [0]
},
'ilp': { 'ilp_count_per_h' : [0],
'ilp_count' : 0,
'ilp_teho' : 0,
'ilp_delay' : 0,
'ulkolampo' : [0], # Näiden logituksen pituudesta päättää siivooja()
'sisalampo' : [0],
'parvi' : [0],
'ulko_puhallus' : [0],
'sisa_puhallus' : [0],
'ilp_ottoteho_log' : [0],
'wc_lampo' : [0],
'varaajain_lampo' : [0],
'lastTimestamp' : 0,
'tuuletin_auto' : 0,
'tuuletin_pwm' : 0,
'tuuletin_parametrit' : 'sp1_c=1 sp1_kW=0.3 sp2_c=2 sp2_kW=0.7 poissa=0 '
},
'lastLogs' : [0]
}
#Tossa on niin vitusti kaikkea turhaa etten ole edes itsekään enään perillä kaikesta
# Vesimittarien kertoimet 1/2 = 11 ja 3/4 = 6.6
devices = {
'huone' : '/sys/bus/w1/devices/28-3cf5f648648b/w1_slave',
'parvi' : '/sys/bus/w1/devices/28-3c05f6482521/w1_slave',
'ulko' : '/sys/bus/w1/devices/28-3c03f648e6af/w1_slave',
'sisa_puhallus' : '/sys/bus/w1/devices/28-3ca9f648c262/w1_slave',
'ulko_puhallus' : '/sys/bus/w1/devices/28-3c0cf6487607/w1_slave'
}
def parsi_lampotila(lines): #Palauttaa arvon 0, jos lämpöä ei mistä tahansa syystä saada
if lines == False or len(lines) != 2:
##logi("parsi_lampotila: EI PARSITTAVAA")
print(lines)
return 0
logi("parsi_lampotila() : " + str(lines))
if (lines[0][-4 : len(lines[0])-1]) != 'YES':
logi("parsi_lampotila: TEMP IS NOT VALID")
logi("txt")
return 0
#print(lines[0][-3 : len(lines[0])])
return round(int(lines[1][lines[1].find("t=")+2:len(lines[1])]) / 1000, 1)
def lue_anturi (device):
##logi("AVATAAN LAITE: " + device)
time.sleep(1)
try:
with open(device, 'r') as onewire:
time.sleep(0.5)
lines = onewire.readlines()
onewire.close()
except:
##logi("lue_anturi: EI LÖYDY LAITETIEDOSTOA")
##logi("lue_anturi: " + device)
time.sleep(1)
return parsi_lampotila(False)
return parsi_lampotila(lines)
def tuulettimen_ohjaus():
#data['ilp']['tuuletin_parametrit'] = 'sp1_c=1 sp1_kW=0.3 sp2_c=3 sp2_kW=0.7'
logi("Tuulettimen parametrit: " + data['ilp']['tuuletin_parametrit'])
nopeudet = (dict(s.split('=', 1) for s in data['ilp']['tuuletin_parametrit'].split()))
lampotilaero = data['ilp']['parvi'][-1] - data['ilp']['sisalampo'][-1]
logi("Lampotilaero: " + str(lampotilaero))
if int(nopeudet['poissa']) == 1:
GPIO.output(21, 1)
GPIO.output(20, 0)
logi("Puhaltimen pieni nopeus - poissa = 1")
return 0
if lampotilaero > float(nopeudet['sp2_c']) or data['ilp']['ilp_teho'] > float(nopeudet['sp2_kW']): #Suurin nopeus
GPIO.output(21, 0)
time.sleep(1)
GPIO.output(21, 1)
GPIO.output(20, 1)
logi("Puhaltimen suuri nopeus")
return 0
if lampotilaero > float(nopeudet['sp1_c']) or data['ilp']['ilp_teho'] > float(nopeudet['sp1_kW']): #Toisiksi suurin nopeus
GPIO.output(21, 1)
GPIO.output(20, 0)
logi("Puhaltimen pieni nopeus")
return 0
GPIO.output(21, 0) #Puhallin kiinni, jos päästään tänne asti
logi("GPIO: 21 = 0, puhallin kiinni")
return 0
def siivooja(): #Poistetaan tarpeeksi vanhat lukemat
global data
if len(data['ilp']['sisalampo']) > 1440: #1440 min == 24 h
data['ilp']['sisalampo'].pop(0)
if len(data['ilp']['ulkolampo']) > 1440:
data['ilp']['ulkolampo'].pop(0)
if len(data['ilp']['parvi']) > 1440:
data['ilp']['parvi'].pop(0)
if len(data['ilp']['sisa_puhallus']) > 1440:
data['ilp']['sisa_puhallus'].pop(0)
if len(data['ilp']['ulko_puhallus']) > 1440:
data['ilp']['ulko_puhallus'].pop(0)
if len(data['ilp']['ilp_ottoteho_log']) > 1440:
data['ilp']['ilp_ottoteho_log'].pop(0)
if len(data['lastLogs']) > 20: #Rivien määrä
data['lastLogs'].pop(0)
if len(data['kulutus_spot']) > 48: #Sähkön kulutus, tunneittain
data['kulutus_spot'].pop(0)
if len(data['ilp']['ilp_count_per_h']) > 24*7*31: #ILP kulutus, tunneittain
data['ilp']['ilp_count_per_h'].pop(0)
if len(data['sahko']['realTimeMonitorLog']) > 50: #Hetkellinen kulutus, rivien määrä
data['sahko']['realTimeMonitorLog'].pop(0)
class handler(BaseHTTPRequestHandler):
global tuloste
def do_OPTIONS(self):
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
#self.send_header("Access-Control-Allow-Credentials", "true")
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Accept")
self.end_headers()
def do_GET(self):
data['meta']['httpPyyntojaSaatu'] += 1
logi("GEttiä kutsutaan!")
if '?tuuletin_parametrit' in self.path:
data['ilp']['tuuletin_parametrit'] = unquote(self.path.split('?tuuletin_parametrit')[1])
logi("Uudet parametrit: " + data['ilp']['tuuletin_parametrit'])
if self.path.endswith('/kaikki_data'): #Lähettää koko data arrayn json dumppina
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
#self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Origin, Accept")
self.send_header('Content-type', 'application/json')
self.end_headers()
output = str.encode(json.dumps(data))
data['meta']['broadcastedBytes'] += len(output)
self.wfile.write(output)
logi("/kaikki_data tarjoiltu")
return
logi("yhteys ei katkea")
elif self.path.endswith('/debug'):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
output = ""
output += pprint.pformat(data, indent=1,sort_dicts=False) #Prints multidimensional array or list very very pretty - with tabs and <br>s on browser. Fucking hard find this on simple without anything functions or loops
self.wfile.write(str.encode(output))
return
elif self.path.endswith('/pakota_varaaja_paalle'):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
if data['vesi']['varaajan_pakotus'] == False:
data['vesi']['varaajan_pakotus'] = True
else:
data['vesi']['varaajan_pakotus'] = False
return
elif self.path.endswith('/etusivu.html'):
self.send_response(200)
self.send_header('Content-type', 'text/html; charset=UTF-8')
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
f = open("etusivu.html", "r")
output = f.read()
f.close()
data['meta']['broadcastedBytes'] += len(output)
self.wfile.write(str.encode(output))
return
elif self.path.endswith('/livestream'):
data['meta']['asiakkaitaPaikalla'] += 1
#Soketti pidetään auki jatkuvasti ja lähetetään selaimelle SSE tyylillä *** MUISTA ALKUUN 'data: ' teksti ja loppuun KAKSI rivinvaihtoa
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
#self.send_header("Access-Control-Allow-Credentials", "true")
self.send_header('Cache-Control:', 'no-cache')
self.send_header('Content-type', 'text/event-stream')
self.end_headers()
edellinen_lastLog = 0
while True: #Valikoitua dataa lähetetään sekunnin välein selaimelle
lastLog = 0
if data['lastLogs'][len(data['lastLogs']) - 1] != edellinen_lastLog: #Ei lähetetä samaa lastlogia uudestaan
lastLog = data['lastLogs'][len(data['lastLogs']) - 1]
edellinen_lastLog = data['lastLogs'][len(data['lastLogs']) - 1]
tunti = int(((datetime.datetime.today()).strftime('%-H')))
output = ('data: ' + json.dumps({
'realTimeMonitorLog' : data['sahko']['realTimeMonitorLog'][ len(data['sahko']['realTimeMonitorLog']) - 1 ],
'meta' : data['meta'],
'lastLogs' : lastLog,
'pulsseja_laskettu' : data['kumul_pulssit'],
'kumul_spot_kokonaishinta' : data['kumul_spot_kokonaishinta'] + (data['piirturi_pulssit'] * ( 1 / data['sahko']['pulsseja_per_kwh'])) * data['sahko_hinta_tanaan'][tunti],
'vesi' : data['vesi'],
'ilp_count' : data['ilp']['ilp_count'] + sum(data['ilp']['ilp_count_per_h']),
'ilp_teho' : data['ilp']['ilp_teho'],
'ulkolampo' : data['ilp']['ulkolampo'][-1],
'sisalampo' : data['ilp']['sisalampo'][-1],
'parvi' : data['ilp']['parvi'][-1],
'ulko_puhallus' : data['ilp']['ulko_puhallus'][-1],
'sisa_puhallus' : data['ilp']['sisa_puhallus'][-1],
'varaajan_karkitieto' : int(GPIO.input(25)), #Varaaja sammuu, kun relepaketti vetää
'pulssit_paiva' : data['sahko']['pulssit_paiva'],
'pulssit_yo' : data['sahko']['pulssit_yo'],
'piirturi_pulssit' : data['piirturi_pulssit']
})
+ "\n\n")
output = str.encode(output)
data['meta']['broadcastedBytes'] += len(output)
try:
self.wfile.write(output)
except:
logi("SOKETTI TAISI KATKETA")
data['meta']['asiakkaitaPaikalla'] -= 1
return
#print(output)
time.sleep(1)
logi("YHTEYS KATKESI")
else:
self.send_response(404)
#self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
output = "<h1>ETUSIVU</h1>"
self.wfile.write(str.encode(output))
print("TÄnne ei pitäisi päästä") #Joskus tänne pääsee silti :O
def kodin_hengetar():
logi("Kodin hengetär täällä!")
global data, f, devices
#testi = 0
ulkokierto_count_temp = 0
kierronSaastoTemp = time.time() #TÄMÄ RIVI ON VAIN MUUTTUJAN KERTALUONTEINEN ALUSTUS
while True:
ulkokierto_count_temp = data['vesi']['ulkokierto_count'] # Heti alussa talteen nykyhetken mittarilukema
pysayta_mittari()
time.sleep(1)
#data['meta']['paivamaaraTanaan'] = datetime.datetime.today().strftime("%d-%m-%Y")
data['meta']['data_tumple_size'] = sys.getsizeof(data)
if data['meta']['paivamaaraTanaan'] != datetime.datetime.today().strftime("%d-%m-%Y"):
#testi = 0
logi("PAIVA VAIHTUI")
data['vesi_logi']['kylma_paivittain'].append((data['vesi']['kylma_vesi_count'] / 6.6) / 60)
data['vesi']['kylma_vesi_count'] = 0
data['vesi_logi']['lammin_paivittain'].append((data['vesi']['ulkokierto_count'] / 11) / 60)
data['vesi']['ulkokierto_count'] = 0
data['meta']['paivamaaraTanaan'] = datetime.datetime.today().strftime("%d-%m-%Y")
#testi += 1
if data['meta']['nordpoolReqTimes'] > 100: #Turvafeature, ettei syystä tai toisesta virhetilanteessa ala mennä spämmiä nordpooliin pyyntöjä
data['meta']['allowNordpoolReq'] = 0
logi("NORDPOOL = 0")
return 0
if data['sahko_hinta_tanaan'][24] != datetime.datetime.today().strftime('%d-%m-%Y') and data['meta']['allowNordpoolReq'] == 1:
#Viimeinen indeksi on päivämäärä ^^
logi("Haetaan tämän päivän sähkön hinta (sahkon_hinta_tanaan 24 = " + str(data['sahko_hinta_tanaan'][24]))
data['sahko_hinta_tanaan'] = hae_sahkonhinta(data['meta']['paivamaaraTanaan']).copy()
data['sahko_hinta_huomenna']
logi("Haettiin tämän päivän sähkönhinta")
logi(data['sahko_hinta_tanaan'][24])
data['sahko_hinta_huomenna'] = [0 for i in range(25)]
huominen = (datetime.datetime.today()+datetime.timedelta(days=+1)).strftime('%d-%m-%Y')
if data['sahko_hinta_huomenna'][24] != huominen and int(time.strftime("%H")) > 15 and data['meta']['allowNordpoolReq'] == 1:
logi("Haetaan huomisen sähkön hinnat:")
data['sahko_hinta_huomenna'] = hae_sahkonhinta(huominen).copy()
logi("HAETTIIN HUOMISEN HINNAT")
data['meta']['saikeitaKaynnissa'] = threading.active_count()
laske_sahkon_kulutus()
#data['vesi']['kierron_paluulampo'] = mittaa_lampotila()
#data['vesi']['kierron_lammitysteho'] = (((data['vesi']['taajuus'] / 11)/60) * (data['vesi']['kierron_paluulampo'] - data['vesi']['kylma_vesi_c']) * 4.2) #4.2 = veden ominaislämpökapasiteetti
#Kaava benchmnarkattu ^^
##### VARAAJAN HOITO ####
data['vesi']['varaajan_status'] -= data['vesi']['ulkokierto_count'] - ulkokierto_count_temp # Vähennetään varaajan statuksesta kierroksen aikana käytetty vesimäärä
if data['vesi']['varaajan_status'] > data['vesi']['varaajan_kapasiteetti']: # Varaaja on täynnä, tämä ettei varaajan_status ylitäyty
data['vesi']['varaajan_status'] = data['vesi']['varaajan_kapasiteetti']
logi("Varaaja on täynnä")
if (data['vesi']['varaajan_status'] < 500):
#Varaajan status ei voi mennä nollaan tai negatiiviseksi
logi("Varaajan_status lähestyy nollaa!")
logi("Muutetaan statukseksi 5000")
data['vesi']['varaajan_status'] = 5000
#data['vesi']['kierron_saasto'] += (data['vesi']['kierron_lammitysteho'] / (3600 / (time.time() - kierronSaastoTemp)))
siivooja()
kierronSaastoTemp = time.time() #Tästä saadaan aika kauanko yhteen kierrokseen menee (sek) TÄMÄN PITÄÄ OLLA VIIMEISENÄ
logi("Kodin hengetär loppu")
def hearthbeat(): #Kutsutaan kerran minuutissa tekemään jotain hanttihommia
#if int(((datetime.datetime.today()).strftime('%S'))) % 60 == 0:
offset_aika = time.time()
odotusaika = 60
if 1:
global data, devices
logi(" *** Hearthbeat ***")
#logi("LUETAAN ULKO_PUHALLUS: ")
data['ilp']['ulko_puhallus'].append(lue_anturi(devices['ulko_puhallus']))
#logi("LUETAAN HUONE:")
data['ilp']['sisalampo'].append(lue_anturi(devices['huone']))
#logi("LUETAAN ULKO:")
data['ilp']['ulkolampo'].append(lue_anturi(devices['ulko']))
data['ilp']['ilp_ottoteho_log'].append(data['ilp']['ilp_teho']) #ILPin hetkellinen ottoteho mittausten aikana
#logi("LUETAAN PARVI: ")
data['ilp']['parvi'].append(lue_anturi(devices['parvi']))
#logi("LUETAAN SISA_PUHALLUS: ")
data['ilp']['sisa_puhallus'].append(lue_anturi(devices['sisa_puhallus']))
###VARAAJA / MINUUTEITTAIN TEHTÄVÄT TOIMET ####
if data['vesi']['varaajan_pakotus'] == True: # Jos varaaja on pakotettu päälle
GPIO.output(25, 1)
logi("Varaaja on pakotettuna päälle")
time.sleep(1)
if GPIO.input(25) == 1: #Lisätään lämpöä vain, jos varaajan kela vetää. Tämä ehto toimii irrallaan kaikesta muusta
heatDiff = 85 - data['vesi']['kylma_vesi_c'] #Veden lämpötilan nousun määrä asteina kaivokylmästä
heatPulsesPerMin = ((data['vesi']['varaajan_lammitysteho'] * 60) / heatDiff / 4.2) * 11 * 60
#Muunnos (kW --> Wh) / montako pulssia X lämpöistä vettä tulee Y teholla 60 sek aikana
data['vesi']['varaajan_status'] += heatPulsesPerMin #Lisätään varaajaan lämmintä
#Varaajasta poistetaan aina häviö, tilasta riippumatta, ennalta määritetyllä teholla:
data['vesi']['varaajan_status'] -= ((data['vesi']['varaajan_havioteho'] * 60) / (85 - 20) / 4.2) * 11 * 60 #Häviöteho, montako litraa lämmitetään 85 - huonelämpö, minuutin aikana X teholla
#Montako pulssia (litraa) status laskee X häviöteholla, lämpötilaeron ollessa Y
#Memo: ajattele asiaa näin: "montako litraa vettä lämpenee esim. 60 astetta tietyllä teholla MINUUTIN AIKANA" --> saat litramäärän --> muuta se vielä pulssien lukumääräksi vesimittarin kertoimella (11)
if (data['vesi']['varaajan_status'] / data['vesi']['varaajan_kapasiteetti']) * 100 < 30 and GPIO.input(25) == 0:
#Jos varaajan status on alle X % JA varaaja ei vielä ole päällä (päiväsaika), pakotetaan se päälle automaattisesti
#Tämän ehdon ei pitäisi toteutua yöllä - kärkitila on silloin 1 - varaaja on jo valmiiksi päällä
data['vesi']['varaajan_pakotus'] = True
logi("Varaaja pakotettiin automaattisesti päälle!")
#tuulettimen_ohjaus()
try:
with open("demofile3.txt", "w") as f:
f.write(json.dumps(data))
f.close()
except:
logi("HEARTHBEAT EI ONNISTUNUT TALLENNUKSESSA")
logi("Hearthbeat - Kutsutaan laskuria: ")
odotusaika = 60 - (round(time.time() - offset_aika)) #Vähennetään minuutin odotusajasta näihin kulunut aika
if odotusaika < 10:
logi("ODOTUSAIKA ALLE 10 SEK")
t = threading.Timer(odotusaika, hearthbeat)
t.start()
def hae_sahkonhinta(paiva = datetime.date.today().strftime('%d-%m-%Y')):
global data
data['meta']['nordpoolReqTimes'] += 2
hinnat = [0 for i in range(25)] #Viimeinen indeksi on päivämäärä
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36'}
osoite = "http://www.nordpoolgroup.com/api/marketdata/page/10?endDate=" + paiva
try:
url = requests.get(osoite, headers = headers)
except:
logi("NORDPOOL ERROR 500")
data['meta']['allowNordpoolReq'] = 0
return hinnat
vastaus = json.loads(url.text)
logi("Nordpool api: " + osoite)
for x in range(24):
hinta = vastaus["data"]["Rows"][x]["Columns"][5]["Value"]
#print(tunti, " ", hinta)
if hinnat[x] == ",":
hinnat[x] = 0
continue
hinnat[x] = float(hinta.replace(",", "."))
hinnat[x] = round((hinnat[x] / 10), 4)
logi("HAETTIIN HINNAT: " + vastaus["endDate"])
hinnat[24] = vastaus["endDate"].strip()
return hinnat
def vaihtuiko_paiva():
global data
if data['meta']['paivamaaraTanaan'] == datetime.datetime.today().strftime("%d-%m-%Y"):
return False
else:
data['meta']['paivamaaraTanaan'] = datetime.datetime.today().strftime("%d-%m-%Y")
return True
logi("Päästiin tänne asti")
def laske_sahkon_kulutus():
global data
#teho = round((((1000/(data["sahko"]["pulsseja_per_kwh"] / data["sahko"]["pulssien_otanta"]))*3600)/data["sahko"]["pulssien_vali"])/1000, 3)
if time.time() - data['sahko']['lastTimestamp'] < 2:
teho = round((((1000/10000)*3600)/data['sahko']['pulssien_vali'])/1000, 3)
else:
teho = 0
logi("Teho näyttää nollaa")
#print(teho)
minuutti = int(((datetime.datetime.today()).strftime('%-M')))
tunti = int(((datetime.datetime.today()).strftime('%-H'))) # -1 = Nordpool spot prices is from 0 index
if minuutti % 60 == 0 and data['piirturi_count'] > 120: # SUORITETAAN TASATUNNEITTAIN, ja vain kerran
spot_hinta_nyt = data['sahko_hinta_tanaan'][tunti] * data['elspot_alv'] #alv prosentteina
data['kulutus_spot'] .append(round(data['piirturi_pulssit'] * ( 1 / data['sahko']['pulsseja_per_kwh']), 3))
#Summataan kumulatiivisesti joka tunti kulutus * pörssihinta:
data['kumul_spot_kokonaishinta'] += (data['piirturi_pulssit'] * ( 1 / data['sahko']['pulsseja_per_kwh'])) * spot_hinta_nyt #Lisätään tämän tunnin kulutus x elspot hinta kokonaishintaan
####### ILP ######
data['ilp']['ilp_count_per_h'].append(data['ilp']['ilp_count'])
data['ilp']['ilp_count'] = 0
logi("TUNTI NUMERO: " + str(tunti))
#
#Varaajan kontaktoria ohjataan avautuvalla koskettimella, varaaja sammuu päivällä vain erikseen käskytettäessä pinout 1:lle
#Yötariffi alkaen klo. 22:00 - päättyen 07:00
if tunti < 7 or tunti >= 22:
#Operaattorina OR koska tunnit on joko alle TAI yli
logi("YÖSÄHKÖN TUNTI")
data['vesi']['varaajan_pakotus'] = False #Poistaa päällepakotuksen, varaaja menee päälle muutenkin nyt, pakotus ei ole voimassa seuraavana päivänä
GPIO.output(25, 1) #Lähdön tila = 1, varaajan kontaktori sulkeutuu
logi("Varaaja = päällä")
data['sahko']['pulssit_yo'] += data['piirturi_pulssit'] # Summataaan Yötunnin pulssit yötariffille
if (tunti >= 7 and tunti < 22): #Päivätariffin ehto
#Operaattorina AND koska muuten ehto toteutuisi myös klo. 22 jälkeisiltä tunneilta
logi("PÄIVÄSÄHKÖN TUNTI")
data['sahko']['pulssit_paiva'] += data['piirturi_pulssit'] #Summataan Päivätunnin pulssit päivätariffille
if data['vesi']['varaajan_pakotus'] == False: #Jos päällepakotus EI ole päällä
GPIO.output(25, 0) #Lähdön tila = 0, varaajan sammuu
logi("Varaaja = kiinni")
else: #Jos halutaan tehdä jotain tässäkohtaa ...
logi("Varaaja on pakotettuna päälle päivällä! Ei tehty mitään!")
##################
logi("TUNTI nro. " + str(tunti) + "vaihtunut!")
logi("Varaajan kärkitila: " + str(GPIO.input(25)))
data['piirturi_pulssit'] = 0 #Nollataan pulssilaskuri uutta tuntia varten
data['piirturi_count'] = 0 #Varmistetaan ettei tätä tasatunnin vaihtumista suoriteta heti uudestaan, vaikka eletään samaa tasaminuuttia
data['piirturi_count'] += 1
#Lisätään joka kierroksella 1, jotta tasatunnin vaihtumista ei kutsuta uudestaan, vaikka minuutti pysyy samana
data['sahko']['realTimeMonitorLog'].append(teho)
####### ILPin hetkellinen teho #############
if data['ilp']['ilp_delay'] > 0:
data['ilp']['ilp_teho'] = round((((1000/1000)*3600)/data['ilp']['ilp_delay'])/1000, 3)
def vesimittari(channel): #Keskeytyshaaskat kutsuu aina tämän paikalle, jos GPIO nastoissa tapahtuu jotain
global data #Keskeytyksissä summataan pulssit SEKÄ lasketaan taajuus
if channel == 17:
data['sahko']['pulssien_vali'] = time.time() - data['sahko']['lastTimestamp']
data['kumul_pulssit'] += 1
data['piirturi_pulssit'] += 1
data['sahko']['lastTimestamp'] = time.time()
return 0
if channel == 26:
data['vesi']['taajuus'] = 1 / (time.time() - data['vesi']['lastTimestamp'])
data['vesi']['lastTimestamp'] = time.time()
data['vesi']['ulkokierto_count'] += 1
return 0
if channel == 13:
data['vesi']['kylma_vesi_taajuus'] = 1 / (time.time() - data['vesi']['kylma_vesi_lastTimestamp'])
data['vesi']['kylma_vesi_lastTimestamp'] = time.time()
data['vesi']['kylma_vesi_count'] += 1
return 0
if channel == 27:
data['ilp']['ilp_delay'] = time.time() - data['ilp']['lastTimestamp']
data['ilp']['ilp_count'] += 1
data['ilp']['lastTimestamp'] = time.time()
def pysayta_mittari(): #Kun virtaus pysähtyy, ei keskeytystä enään tule == ei jätetä edellistä taajuuslukemaa päälle
global data
if time.time() - data['vesi']['lastTimestamp'] > 1:
data['vesi']['taajuus'] = 0
if time.time() - data['vesi']['kylma_vesi_lastTimestamp'] > 1:
data['vesi']['kylma_vesi_taajuus'] = 0
if time.time() - data['ilp']['lastTimestamp'] > 360:
data['ilp']['lastTimestamp'] = time.time()
logi("Nollattiin ILPin mittari")
data['ilp']['ilp_teho'] = 0
#if time.time() - data['sahko']['lastTimestamp'] > 1:
# data['sahko']['pulssien_vali'] = False
def mittaa_lampotila():
global f
val_2 = ADS.readADC(0)
val_1 = ADS.readADC(2)
jannite = val_2 * f
vastus = round(47000 / ((val_1 * f) / jannite -1), 2)
#print("Analog2: {0:d}\t{1:.3f} V".format(val_2, jannite))
#B VALUE = 3950
lampo = (1 / (1 / 298.16 +1 / 3950 * ln(vastus / 50000))) - 273.15
#print(lampo , "vastus: " , vastus, "3.3 jannite: ", val_1 * f)
return lampo
def logi(txt):
print("[", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), " ] ", txt)
data['lastLogs'].append("[" + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "] " + txt + "")
time.sleep(1)
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
logi("Threaded Mixin luokkaa kutsutaan!")
"""Handle requests in a separate thread."""
def create_server():
port = 3323
global data, vesi
httpd = ThreadedHTTPServer(("0.0.0.0", port), handler)
logi("HTTP KUUNTELEE PORTISSA " + str(port))
httpd.serve_forever()
try: #Jos data array on aiemmin dumpattu tiedostoon talteen
with open("demofile3.txt", "r") as file:
logi("TIEDOSTO LÖYTYI")
data = json.loads(file.read())
file.close()
except:
logi("Ei tiedostoa")
#ADS = ADS1x15.ADS1115(1, 0x48)
#ADS.setGain(ADS.PGA_4_096V)
#f = ADS.toVoltage()
#ADS.setDataRate(0)
#Haaskat odottamassa:
benchmark = False
if benchmark == False:
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #Kuuma vesi
GPIO.add_event_detect(26, GPIO.FALLING, bouncetime=2)
GPIO.add_event_callback(26, vesimittari)
GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #Kylmä vesi
GPIO.add_event_detect(13, GPIO.FALLING, bouncetime=2)
GPIO.add_event_callback(13, vesimittari)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #Sähkömittari
GPIO.add_event_detect(17, GPIO.FALLING, bouncetime=2)
GPIO.add_event_callback(17, vesimittari)
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # ILP sähkömittari
GPIO.add_event_detect(27, GPIO.FALLING, bouncetime=30)
GPIO.add_event_callback(27, vesimittari)
#GPIO.setup(21, GPIO.OUT, initial=GPIO.LOW)
#GPIO.setup(20, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(25, GPIO.OUT, initial=GPIO.LOW)
#Varaajan kärki. Avautuva kosketin = oletuksena varaajan kontaktorille avautuva kosketin, varaaja silloin päällä
for i in range(5):
time.sleep(1)
logi("odotetaan " + str(i))
#Kodin hengetär tekee ns. raskaat, aikaa vievät työt ja paskahommat
#Sekunttikierto laskee veden kulutukset ja sen keston täytyy olla tarkasti sekuntti
time.sleep(1)
_thread.start_new_thread(kodin_hengetar, tuple())
logi("Kodin hengerät käynnistetty!")
hearthbeat()
logi("hearthbeat() käynnistetty!")
time.sleep(1)
_thread.start_new_thread(create_server, tuple())
data['meta']['restartTimes'] += 1
kaynnistysaika = math.ceil(time.time())
try:
start = time.time()
while True: #Mainloop ei tee muuta kuin odottaa pysäytyskäskyä näppikseltä
#print(data["vesi"]["ulkokierto_count"])
#print(data["sahko"]["pulssien_vali"])
#time.sleep(1 / 183.333)
#vesimittari(26)
#print(time.time() - start)
time.sleep(2)
except KeyboardInterrupt:
logi("tallennetaan data")
file = open("demofile3.txt", "w")
file.write(json.dumps(data))
file.close()
data['meta']['ajoaikaSec'] += (math.ceil(time.time()) - kaynnistysaika)
print("\nTidying up")