Facebook
From Emerald Matamata, 1 Year ago, written in Python.
Embed
Download Paste or View Raw
Hits: 54
  1. import RPi.GPIO as GPIO
  2. import time, sys
  3. import http.server
  4. from http.server import BaseHTTPRequestHandler, HTTPServer
  5. import socketserver
  6. import _thread
  7. import threading
  8. import socket
  9. from socketserver import ThreadingMixIn
  10. import requests, json
  11. import datetime
  12. import array as arr
  13. import statistics
  14. import time
  15. import json
  16. import pprint
  17. import math
  18. import os
  19. import ADS1x15
  20. from timeit import Timer
  21. from numpy import log as ln
  22. from urllib.parse import unquote
  23.  
  24.  
  25. data = {
  26.         'sahko_hinta_tanaan'             :  [0 for i in range(25)] ,
  27.         'sahko_hinta_huomenna'           :  [0 for i in range(25)] ,
  28.         'kulutus_spot'                   :  [0],
  29.         'spot_kulutus_hinta'             :  [0],
  30.         'kumul_spot_kokonaishinta'        :  0,
  31.         'kumul_kokonaiskulutus'          :  0,
  32.         'kumul_pulssit'                  :  0,
  33.         'elspot_alv'                     : 1.193, #HOX Nordpoolin hinnat ovat verottomia. HOX ALV lasketaan verottomasta hinnasta ;)
  34.         'elspot_marginaali'              : 0.01,
  35.         'piirturi_sahko'                 :  [0],
  36.         'piirturi_pulssit'               : 0,
  37.         'piirturi_count'                 : 1000, #Piirturi aloittaa seuraavasta tasa-vartista
  38.         'siirtoHintaYosahkolla'          : 0,
  39.         'sahko' :  {'pulsseja_laskettu'  : 0,
  40.                     'pulsseja_q'          : 0,
  41.                     'current_q'             : 0,
  42.                     'aloitusaika'        : time.time(),
  43.                     'pulssien_vali'      : 10,
  44.                     'pulssien_otanta'    : 5,
  45.                     'aikaleimat'         : 0,
  46.                     'realTimeMonitorLog' : [0],
  47.                     'pulssit_yo'         : 0,
  48.                     'pulssit_paiva'      : 0,
  49.                     'lastTimestamp'      : 0,
  50.                     'pulsseja_per_kwh'   : 10000    # yes, Mäntsälän Sähkö aivilon have 10 000 blinks per kWh ;)
  51.                     },
  52.         'kv'    :   {'pulsseja_laskettu' : 0,       #Täysin turha homma mutta en mene nyt sorkkimaan sitä
  53.                      'vetta_kulunut'     : 0,
  54.                      'aloitusaika'       : time.time(),
  55.                      'pulssien_vali'     : 0
  56.                     },
  57.         'meta'  :   {'saikeitaKaynnissa' : 0,
  58.                      'asiakkaitaPaikalla': 0,
  59.                      'data_tumple_size'  : 44,  #Mikshän tuo on noin ?? Kysyisin itseltäin
  60.                      'broadcastedBytes'  : 0,
  61.                      'nordpoolReqTimes'  : 0,
  62.                      'httpPyyntojaSaatu' : 0,
  63.                      'allowNordpoolReq'  : 1,   #Vahditaan hieman ettei ala spämmiä Nordpooliin. Kerran jo pelastikin kun tein virheen koodiin
  64.                      'paivamaaraTanaan'  : datetime.datetime.today().strftime("%d-%m-%Y"),
  65.                      'restartTimes'      : 0,
  66.                      'ajoaikaSec'        : 0
  67.                     },
  68.         'vesi' :    {'taajuus'                   : 0,
  69.                      'lastTimestamp'             : 0,
  70.                      'ulkokierto_count'          : 0,
  71.                      'kierron_lammitysteho'      : 0,
  72.                      'kierron_saasto'            : 0,
  73.                      'kierron_paluulampo'        : 0,
  74.                      '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
  75.                      'kylma_vesi_taajuus'        : 0,
  76.                      'kylma_vesi_lastTimestamp'  : 0,
  77.                      'kylma_vesi_count'          : 0,
  78.                      'varaajan_kapasiteetti'     : 99000, # 99000 pulssia mittarilta = 150 litraa,
  79.                      'varaajan_havioteho'        : 0.1,  # kW  
  80.                      'varaajan_lammitysteho'     : 1.92,   # kW
  81.                      'varaajan_status'           : 99000, # Lähtötilanne = täynnä
  82.                      'varaajan_pakotus'          : False  # True = pinout lähtö on silloin 0, avautuva kosketin ohjaa kontaktorin kelaa
  83.                     },
  84.         'vesi_logi': { 'kylma_paivittain': [0],         #HOX tätä ei striimata livenä, eritelty siksi veden alaisuudesta pois
  85.                        'lammin_paivittain': [0]
  86.                      },
  87.         'ilp':       { 'ilp_count_per_h'          : [0],
  88.                        'ilp_count'                : 0,
  89.                        'ilp_teho'                 : 0,
  90.                        'ilp_delay'                : 0,
  91.                        'ulkolampo'                : [0],    # Näiden logituksen pituudesta päättää siivooja()
  92.                        'sisalampo'                : [0],
  93.                        'parvi'                    : [0],
  94.                        'ulko_puhallus'            : [0],
  95.                        'sisa_puhallus'            : [0],
  96.                        'ilp_ottoteho_log'         : [0],
  97.                        'wc_lampo'                 : [0],
  98.                        'varaajain_lampo'          : [0],
  99.                        'lastTimestamp'            : 0,
  100.                        'tuuletin_auto'            : 0,
  101.                        'tuuletin_pwm'             : 0,
  102.                        'tuuletin_parametrit'      : 'sp1_c=1 sp1_kW=0.3 sp2_c=2 sp2_kW=0.7 poissa=0 '
  103.                     },
  104.         'lastLogs' : [0]
  105.                        
  106.     }
  107. #Tossa on niin vitusti kaikkea turhaa etten ole edes itsekään enään perillä kaikesta
  108. # Vesimittarien kertoimet 1/2 = 11 ja 3/4 = 6.6
  109.  
  110. devices = {
  111.         'huone'             : '/sys/bus/w1/devices/28-3cf5f648648b/w1_slave',
  112.         'parvi'             : '/sys/bus/w1/devices/28-3c05f6482521/w1_slave',
  113.         'ulko'              : '/sys/bus/w1/devices/28-3c03f648e6af/w1_slave',
  114.         'sisa_puhallus'     : '/sys/bus/w1/devices/28-3ca9f648c262/w1_slave',
  115.         'ulko_puhallus'     : '/sys/bus/w1/devices/28-3c0cf6487607/w1_slave'
  116. }
  117.  
  118. def parsi_lampotila(lines): #Palauttaa arvon 0, jos lämpöä ei mistä tahansa syystä saada
  119.        
  120.  
  121.     if lines == False or len(lines) != 2:
  122.         ##logi("parsi_lampotila: EI PARSITTAVAA")
  123.         print(lines)
  124.         return 0
  125.     logi("parsi_lampotila() : " + str(lines))
  126.     if (lines[0][-4 : len(lines[0])-1]) != 'YES':
  127.         logi("parsi_lampotila: TEMP IS NOT VALID")
  128.         logi("txt")
  129.         return 0
  130.     #print(lines[0][-3 : len(lines[0])])
  131.     return round(int(lines[1][lines[1].find("t=")+2:len(lines[1])]) / 1000, 1)
  132.  
  133.  
  134. def lue_anturi (device):
  135.     ##logi("AVATAAN LAITE: " + device)
  136.     time.sleep(1)
  137.     try:
  138.         with open(device, 'r') as onewire:
  139.             time.sleep(0.5)
  140.             lines = onewire.readlines()
  141.             onewire.close()
  142.     except:
  143.         ##logi("lue_anturi: EI LÖYDY LAITETIEDOSTOA")
  144.         ##logi("lue_anturi: " + device)
  145.         time.sleep(1)
  146.         return parsi_lampotila(False)
  147.     return parsi_lampotila(lines)
  148.    
  149. def tuulettimen_ohjaus():
  150.     #data['ilp']['tuuletin_parametrit'] = 'sp1_c=1 sp1_kW=0.3 sp2_c=3 sp2_kW=0.7'
  151.     logi("Tuulettimen parametrit: " + data['ilp']['tuuletin_parametrit'])
  152.     nopeudet = (dict(s.split('=', 1) for s in data['ilp']['tuuletin_parametrit'].split()))
  153.    
  154.     lampotilaero = data['ilp']['parvi'][-1] - data['ilp']['sisalampo'][-1]
  155.     logi("Lampotilaero: " + str(lampotilaero))
  156.     if int(nopeudet['poissa']) == 1:
  157.         GPIO.output(21, 1)
  158.         GPIO.output(20, 0)
  159.         logi("Puhaltimen pieni nopeus - poissa = 1")
  160.         return 0
  161.     if lampotilaero > float(nopeudet['sp2_c']) or data['ilp']['ilp_teho'] > float(nopeudet['sp2_kW']): #Suurin nopeus
  162.         GPIO.output(21, 0)
  163.         time.sleep(1)
  164.         GPIO.output(21, 1)
  165.         GPIO.output(20, 1)
  166.         logi("Puhaltimen suuri nopeus")
  167.         return 0
  168.     if lampotilaero > float(nopeudet['sp1_c']) or data['ilp']['ilp_teho'] > float(nopeudet['sp1_kW']):  #Toisiksi suurin nopeus
  169.         GPIO.output(21, 1)
  170.         GPIO.output(20, 0)
  171.         logi("Puhaltimen pieni nopeus")
  172.         return 0
  173.     GPIO.output(21, 0) #Puhallin kiinni, jos päästään tänne asti
  174.     logi("GPIO: 21 = 0, puhallin kiinni")
  175.     return 0
  176.        
  177.  
  178. def siivooja(): #Poistetaan tarpeeksi vanhat lukemat
  179.     global data
  180.     if len(data['ilp']['sisalampo']) > 1440:         #1440 min == 24 h
  181.         data['ilp']['sisalampo'].pop(0)
  182.     if len(data['ilp']['ulkolampo']) > 1440:
  183.         data['ilp']['ulkolampo'].pop(0)
  184.     if len(data['ilp']['parvi']) > 1440:
  185.         data['ilp']['parvi'].pop(0)
  186.     if len(data['ilp']['sisa_puhallus']) > 1440:
  187.         data['ilp']['sisa_puhallus'].pop(0)
  188.     if len(data['ilp']['ulko_puhallus']) > 1440:
  189.         data['ilp']['ulko_puhallus'].pop(0)
  190.     if len(data['ilp']['ilp_ottoteho_log']) > 1440:
  191.         data['ilp']['ilp_ottoteho_log'].pop(0)
  192.     if len(data['lastLogs']) > 20:                  #Rivien määrä
  193.         data['lastLogs'].pop(0)
  194.     if len(data['kulutus_spot']) > 48:              #Sähkön kulutus, tunneittain
  195.         data['kulutus_spot'].pop(0)
  196.     if len(data['ilp']['ilp_count_per_h']) > 24*7*31: #ILP kulutus, tunneittain
  197.         data['ilp']['ilp_count_per_h'].pop(0)
  198.     if len(data['sahko']['realTimeMonitorLog']) > 50:   #Hetkellinen kulutus, rivien määrä
  199.         data['sahko']['realTimeMonitorLog'].pop(0)
  200.    
  201. class handler(BaseHTTPRequestHandler):
  202.     global tuloste
  203.     def do_OPTIONS(self):
  204.         self.send_response(200)
  205.         self.send_header("Access-Control-Allow-Origin", "*")
  206.         #self.send_header("Access-Control-Allow-Credentials", "true")
  207.         self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
  208.         self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Accept")  
  209.         self.end_headers()
  210.     def do_GET(self):
  211.         data['meta']['httpPyyntojaSaatu'] += 1
  212.         logi("GEttiä kutsutaan!")
  213.         if '?tuuletin_parametrit' in self.path:
  214.             data['ilp']['tuuletin_parametrit'] = unquote(self.path.split('?tuuletin_parametrit')[1])
  215.             logi("Uudet parametrit: " + data['ilp']['tuuletin_parametrit'])
  216.         if self.path.endswith('/kaikki_data'): #Lähettää koko data arrayn json dumppina
  217.             self.send_response(200)
  218.             self.send_header("Access-Control-Allow-Origin", "*")
  219.             #self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Origin, Accept")
  220.             self.send_header('Content-type', 'application/json')
  221.             self.end_headers()
  222.             output = str.encode(json.dumps(data))
  223.             data['meta']['broadcastedBytes'] += len(output)
  224.             self.wfile.write(output)
  225.             logi("/kaikki_data tarjoiltu")
  226.             return
  227.             logi("yhteys ei katkea")
  228.         elif self.path.endswith('/debug'):
  229.             self.send_response(200)
  230.             self.send_header('Content-type', 'text/plain')
  231.             self.send_header("Access-Control-Allow-Origin", "*")
  232.             self.end_headers()
  233.             output = ""
  234.             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
  235.             self.wfile.write(str.encode(output))
  236.             return
  237.         elif self.path.endswith('/pakota_varaaja_paalle'):
  238.             self.send_response(200)
  239.             self.send_header('Content-type', 'text/plain')
  240.             self.send_header("Access-Control-Allow-Origin", "*")
  241.             self.end_headers()
  242.             if data['vesi']['varaajan_pakotus'] == False:
  243.                 data['vesi']['varaajan_pakotus'] = True
  244.             else:
  245.                 data['vesi']['varaajan_pakotus'] = False
  246.             return
  247.         elif self.path.endswith('/etusivu.html'):
  248.             self.send_response(200)
  249.             self.send_header('Content-type', 'text/html; charset=UTF-8')
  250.             self.send_header("Access-Control-Allow-Origin", "*")
  251.             self.end_headers()
  252.             f = open("etusivu.html", "r")
  253.             output = f.read()
  254.             f.close()
  255.             data['meta']['broadcastedBytes'] += len(output)
  256.             self.wfile.write(str.encode(output))
  257.             return
  258.         elif self.path.endswith('/livestream'):
  259.             data['meta']['asiakkaitaPaikalla'] += 1
  260.             #Soketti pidetään auki jatkuvasti ja lähetetään selaimelle SSE tyylillä *** MUISTA ALKUUN 'data: ' teksti ja loppuun KAKSI rivinvaihtoa
  261.             self.send_response(200)
  262.             self.send_header("Access-Control-Allow-Origin", "*")
  263.             #self.send_header("Access-Control-Allow-Credentials", "true")
  264.             self.send_header('Cache-Control:', 'no-cache')
  265.             self.send_header('Content-type', 'text/event-stream')
  266.             self.end_headers()
  267.             edellinen_lastLog = 0
  268.             while True: #Valikoitua dataa lähetetään sekunnin välein selaimelle
  269.                 lastLog = 0
  270.                 if data['lastLogs'][len(data['lastLogs']) - 1] != edellinen_lastLog: #Ei lähetetä samaa lastlogia uudestaan
  271.                     lastLog = data['lastLogs'][len(data['lastLogs']) - 1]
  272.                 edellinen_lastLog = data['lastLogs'][len(data['lastLogs']) - 1]
  273.                 tunti = int(((datetime.datetime.today()).strftime('%-H')))
  274.                 output = ('data: ' + json.dumps({
  275.                                                 'realTimeMonitorLog' : data['sahko']['realTimeMonitorLog'][ len(data['sahko']['realTimeMonitorLog']) - 1 ],
  276.                                                 'meta' :                data['meta'],
  277.                                                 'lastLogs' :            lastLog,
  278.                                                 'pulsseja_laskettu' :   data['kumul_pulssit'],
  279.                                                 'kumul_spot_kokonaishinta' : data['kumul_spot_kokonaishinta'] + (data['piirturi_pulssit'] * ( 1 / data['sahko']['pulsseja_per_kwh'])) * data['sahko_hinta_tanaan'][tunti],
  280.                                                 'vesi' :                data['vesi'],
  281.                                                 'ilp_count' :           data['ilp']['ilp_count'] + sum(data['ilp']['ilp_count_per_h']),
  282.                                                 'ilp_teho' :            data['ilp']['ilp_teho'],
  283.                                                 'ulkolampo' :           data['ilp']['ulkolampo'][-1],
  284.                                                 'sisalampo' :           data['ilp']['sisalampo'][-1],
  285.                                                 'parvi'     :            data['ilp']['parvi'][-1],
  286.                                                 'ulko_puhallus' :       data['ilp']['ulko_puhallus'][-1],
  287.                                                 'sisa_puhallus' :       data['ilp']['sisa_puhallus'][-1],
  288.                                                 'varaajan_karkitieto' : int(GPIO.input(25)),              #Varaaja sammuu, kun relepaketti vetää
  289.                                                 'pulssit_paiva' :       data['sahko']['pulssit_paiva'],
  290.                                                 'pulssit_yo'    :       data['sahko']['pulssit_yo'],
  291.                                                 'piirturi_pulssit' :    data['piirturi_pulssit']
  292.                                                 })
  293.                                                 + "\n\n")
  294.                 output = str.encode(output)
  295.                 data['meta']['broadcastedBytes'] += len(output)
  296.                 try:
  297.                      self.wfile.write(output)
  298.                 except:
  299.                     logi("SOKETTI TAISI KATKETA")
  300.                     data['meta']['asiakkaitaPaikalla'] -= 1
  301.                     return
  302.                 #print(output)
  303.                 time.sleep(1)
  304.             logi("YHTEYS KATKESI")
  305.            
  306.         else:
  307.             self.send_response(404)
  308.             #self.send_header("Access-Control-Allow-Origin", "*")
  309.             self.end_headers()
  310.             output = "<h1>ETUSIVU</h1>"
  311.             self.wfile.write(str.encode(output))
  312.            
  313.     print("TÄnne ei pitäisi päästä")    #Joskus tänne pääsee silti :O
  314.  
  315. def kodin_hengetar():
  316.  
  317.     logi("Kodin hengetär täällä!")
  318.     global data, f, devices
  319.     #testi = 0
  320.     ulkokierto_count_temp = 0
  321.     kierronSaastoTemp = time.time()                                     #TÄMÄ RIVI ON VAIN MUUTTUJAN KERTALUONTEINEN ALUSTUS
  322.     while True:
  323.         ulkokierto_count_temp = data['vesi']['ulkokierto_count']        # Heti alussa talteen nykyhetken mittarilukema
  324.         pysayta_mittari()
  325.         time.sleep(1)
  326.         #data['meta']['paivamaaraTanaan'] = datetime.datetime.today().strftime("%d-%m-%Y")
  327.         data['meta']['data_tumple_size'] = sys.getsizeof(data)
  328.  
  329.         if data['meta']['paivamaaraTanaan'] != datetime.datetime.today().strftime("%d-%m-%Y"):
  330.             #testi = 0
  331.             logi("PAIVA VAIHTUI")
  332.             data['vesi_logi']['kylma_paivittain'].append((data['vesi']['kylma_vesi_count'] / 6.6) / 60)
  333.             data['vesi']['kylma_vesi_count'] = 0
  334.             data['vesi_logi']['lammin_paivittain'].append((data['vesi']['ulkokierto_count'] / 11) / 60)
  335.             data['vesi']['ulkokierto_count'] = 0
  336.             data['meta']['paivamaaraTanaan'] = datetime.datetime.today().strftime("%d-%m-%Y")
  337.        
  338.         #testi += 1
  339.         if data['meta']['nordpoolReqTimes'] > 100:    #Turvafeature, ettei syystä tai toisesta virhetilanteessa ala mennä spämmiä nordpooliin pyyntöjä
  340.             data['meta']['allowNordpoolReq'] = 0
  341.             logi("NORDPOOL = 0")
  342.             return 0
  343.        
  344.         if data['sahko_hinta_tanaan'][24] != datetime.datetime.today().strftime('%d-%m-%Y') and data['meta']['allowNordpoolReq'] == 1:
  345.             #Viimeinen indeksi on päivämäärä ^^
  346.             logi("Haetaan tämän päivän sähkön hinta (sahkon_hinta_tanaan 24 = " + str(data['sahko_hinta_tanaan'][24]))
  347.             data['sahko_hinta_tanaan'] = hae_sahkonhinta(data['meta']['paivamaaraTanaan']).copy()
  348.             data['sahko_hinta_huomenna']
  349.             logi("Haettiin tämän päivän sähkönhinta")
  350.             logi(data['sahko_hinta_tanaan'][24])
  351.             data['sahko_hinta_huomenna'] = [0 for i in range(25)]
  352.            
  353.         huominen = (datetime.datetime.today()+datetime.timedelta(days=+1)).strftime('%d-%m-%Y')
  354.         if data['sahko_hinta_huomenna'][24] != huominen and int(time.strftime("%H")) > 15 and data['meta']['allowNordpoolReq'] == 1:
  355.             logi("Haetaan huomisen sähkön hinnat:")
  356.            
  357.             data['sahko_hinta_huomenna'] = hae_sahkonhinta(huominen).copy()
  358.             logi("HAETTIIN HUOMISEN HINNAT")
  359.        
  360.  
  361.         data['meta']['saikeitaKaynnissa'] = threading.active_count()
  362.        
  363.         laske_sahkon_kulutus()
  364.        
  365.        
  366.         #data['vesi']['kierron_paluulampo'] = mittaa_lampotila()
  367.  
  368.         #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
  369.         #Kaava benchmnarkattu ^^
  370.        
  371.         ##### VARAAJAN HOITO ####
  372.         data['vesi']['varaajan_status'] -= data['vesi']['ulkokierto_count'] - ulkokierto_count_temp     # Vähennetään varaajan statuksesta kierroksen aikana käytetty vesimäärä
  373.  
  374.         if data['vesi']['varaajan_status'] > data['vesi']['varaajan_kapasiteetti']:                     # Varaaja on täynnä, tämä ettei varaajan_status ylitäyty
  375.             data['vesi']['varaajan_status'] = data['vesi']['varaajan_kapasiteetti']
  376.             logi("Varaaja on täynnä")
  377.         if (data['vesi']['varaajan_status'] < 500):
  378.             #Varaajan status ei voi mennä nollaan tai negatiiviseksi
  379.             logi("Varaajan_status lähestyy nollaa!")
  380.             logi("Muutetaan statukseksi 5000")
  381.             data['vesi']['varaajan_status'] = 5000
  382.         #data['vesi']['kierron_saasto'] += (data['vesi']['kierron_lammitysteho'] / (3600 / (time.time() - kierronSaastoTemp)))
  383.         siivooja()
  384.         kierronSaastoTemp = time.time() #Tästä saadaan aika kauanko yhteen kierrokseen menee (sek) TÄMÄN PITÄÄ OLLA VIIMEISENÄ
  385.  
  386.  
  387.     logi("Kodin hengetär loppu")
  388.  
  389.  
  390. def hearthbeat(): #Kutsutaan kerran minuutissa tekemään jotain hanttihommia
  391.     #if int(((datetime.datetime.today()).strftime('%S'))) % 60 == 0:
  392.     offset_aika = time.time()
  393.     odotusaika = 60
  394.     if 1:
  395.         global data, devices
  396.         logi(" *** Hearthbeat ***")
  397.         #logi("LUETAAN ULKO_PUHALLUS: ")
  398.         data['ilp']['ulko_puhallus'].append(lue_anturi(devices['ulko_puhallus']))
  399.         #logi("LUETAAN HUONE:")
  400.         data['ilp']['sisalampo'].append(lue_anturi(devices['huone']))
  401.         #logi("LUETAAN ULKO:")
  402.         data['ilp']['ulkolampo'].append(lue_anturi(devices['ulko']))
  403.         data['ilp']['ilp_ottoteho_log'].append(data['ilp']['ilp_teho'])     #ILPin hetkellinen ottoteho mittausten aikana
  404.         #logi("LUETAAN PARVI: ")
  405.         data['ilp']['parvi'].append(lue_anturi(devices['parvi']))
  406.         #logi("LUETAAN SISA_PUHALLUS: ")
  407.         data['ilp']['sisa_puhallus'].append(lue_anturi(devices['sisa_puhallus']))
  408.        
  409.        
  410.        
  411.         ###VARAAJA / MINUUTEITTAIN TEHTÄVÄT TOIMET ####
  412.         if data['vesi']['varaajan_pakotus'] == True:           # Jos varaaja on pakotettu päälle
  413.             GPIO.output(25, 1)
  414.             logi("Varaaja on pakotettuna päälle")
  415.             time.sleep(1)
  416.         if GPIO.input(25) == 1:                               #Lisätään lämpöä vain, jos varaajan kela vetää. Tämä ehto toimii irrallaan kaikesta muusta
  417.             heatDiff = 85 - data['vesi']['kylma_vesi_c']      #Veden lämpötilan nousun määrä asteina kaivokylmästä
  418.             heatPulsesPerMin = ((data['vesi']['varaajan_lammitysteho'] * 60) / heatDiff / 4.2) * 11 * 60
  419.             #Muunnos (kW --> Wh) / montako pulssia X lämpöistä vettä tulee Y teholla 60 sek aikana
  420.             data['vesi']['varaajan_status'] += heatPulsesPerMin     #Lisätään varaajaan lämmintä
  421.        
  422.         #Varaajasta poistetaan aina häviö, tilasta riippumatta, ennalta määritetyllä teholla:
  423.         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
  424.         #Montako pulssia (litraa) status laskee X häviöteholla, lämpötilaeron ollessa Y
  425.         #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)
  426.        
  427.         if (data['vesi']['varaajan_status'] / data['vesi']['varaajan_kapasiteetti']) * 100 < 30 and GPIO.input(25) == 0:
  428.             #Jos varaajan status on alle X % JA varaaja ei vielä ole päällä (päiväsaika), pakotetaan se päälle automaattisesti
  429.             #Tämän ehdon ei pitäisi toteutua yöllä - kärkitila on silloin 1 - varaaja on jo valmiiksi päällä
  430.             data['vesi']['varaajan_pakotus'] = True
  431.             logi("Varaaja pakotettiin automaattisesti päälle!")
  432.        
  433.        
  434.        
  435.        
  436.         #tuulettimen_ohjaus()
  437.        
  438.        
  439.  
  440.         try:
  441.             with open("demofile3.txt", "w") as f:
  442.                 f.write(json.dumps(data))
  443.                 f.close()
  444.         except:
  445.             logi("HEARTHBEAT EI ONNISTUNUT TALLENNUKSESSA")
  446.     logi("Hearthbeat - Kutsutaan laskuria: ")
  447.     odotusaika = 60 - (round(time.time() - offset_aika))        #Vähennetään minuutin odotusajasta näihin kulunut aika
  448.     if odotusaika < 10:
  449.         logi("ODOTUSAIKA ALLE 10 SEK")
  450.     t = threading.Timer(odotusaika, hearthbeat)
  451.     t.start()
  452.  
  453.  
  454. def hae_sahkonhinta(paiva = datetime.date.today().strftime('%d-%m-%Y')):
  455.     global data
  456.     data['meta']['nordpoolReqTimes'] += 2
  457.     hinnat = [0 for i in range(25)] #Viimeinen indeksi on päivämäärä
  458.     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'}
  459.  
  460.     osoite = "http://www.nordpoolgroup.com/api/marketdata/page/10?endDate=" + paiva
  461.     try:
  462.         url = requests.get(osoite, headers = headers)
  463.     except:
  464.         logi("NORDPOOL ERROR 500")
  465.         data['meta']['allowNordpoolReq'] = 0
  466.         return hinnat
  467.    
  468.     vastaus = json.loads(url.text)
  469.     logi("Nordpool api: " + osoite)
  470.    
  471.     for x in range(24):    
  472.         hinta = vastaus["data"]["Rows"][x]["Columns"][5]["Value"]
  473.         #print(tunti, " ", hinta)
  474.         if hinnat[x] == ",":
  475.             hinnat[x] = 0
  476.             continue
  477.         hinnat[x] = float(hinta.replace(",", "."))
  478.         hinnat[x] = round((hinnat[x] / 10), 4)
  479.     logi("HAETTIIN HINNAT: " + vastaus["endDate"])
  480.     hinnat[24] = vastaus["endDate"].strip()
  481.    
  482.     return hinnat
  483.  
  484. def vaihtuiko_paiva():
  485.     global data
  486.     if data['meta']['paivamaaraTanaan'] == datetime.datetime.today().strftime("%d-%m-%Y"):
  487.         return False
  488.     else:
  489.         data['meta']['paivamaaraTanaan'] = datetime.datetime.today().strftime("%d-%m-%Y")
  490.         return True
  491.     logi("Päästiin tänne asti")
  492.  
  493. def laske_sahkon_kulutus():
  494.     global data
  495.     #teho = round((((1000/(data["sahko"]["pulsseja_per_kwh"] / data["sahko"]["pulssien_otanta"]))*3600)/data["sahko"]["pulssien_vali"])/1000, 3)
  496.    
  497.     if time.time() - data['sahko']['lastTimestamp'] < 2:
  498.         teho = round((((1000/10000)*3600)/data['sahko']['pulssien_vali'])/1000, 3)
  499.     else:
  500.         teho = 0
  501.         logi("Teho näyttää nollaa")
  502.     #print(teho)
  503.     minuutti = int(((datetime.datetime.today()).strftime('%-M')))
  504.     tunti = int(((datetime.datetime.today()).strftime('%-H'))) # -1 = Nordpool spot prices is from 0 index
  505.  
  506.     if minuutti % 60 == 0 and data['piirturi_count'] > 120: # SUORITETAAN TASATUNNEITTAIN, ja vain kerran
  507.         spot_hinta_nyt = data['sahko_hinta_tanaan'][tunti] * data['elspot_alv']     #alv prosentteina
  508.         data['kulutus_spot']                .append(round(data['piirturi_pulssit'] * ( 1 / data['sahko']['pulsseja_per_kwh']), 3))
  509.         #Summataan kumulatiivisesti joka tunti kulutus * pörssihinta:
  510.         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
  511.  
  512.  
  513.         ####### ILP ######
  514.         data['ilp']['ilp_count_per_h'].append(data['ilp']['ilp_count'])
  515.         data['ilp']['ilp_count'] = 0
  516.        
  517.        
  518.         logi("TUNTI NUMERO: " + str(tunti))
  519.         #
  520.         #Varaajan kontaktoria ohjataan avautuvalla koskettimella, varaaja sammuu päivällä vain erikseen käskytettäessä pinout 1:lle
  521.         #Yötariffi alkaen klo. 22:00 - päättyen 07:00
  522.         if tunti < 7 or tunti >= 22:
  523.             #Operaattorina OR koska tunnit on joko alle TAI yli
  524.             logi("YÖSÄHKÖN TUNTI")
  525.             data['vesi']['varaajan_pakotus'] = False                    #Poistaa päällepakotuksen, varaaja menee päälle muutenkin nyt, pakotus ei ole voimassa seuraavana päivänä
  526.             GPIO.output(25, 1)                                          #Lähdön tila = 1, varaajan kontaktori sulkeutuu
  527.             logi("Varaaja = päällä")
  528.             data['sahko']['pulssit_yo'] += data['piirturi_pulssit']     # Summataaan Yötunnin pulssit yötariffille
  529.            
  530.         if (tunti >= 7 and tunti < 22):    #Päivätariffin ehto
  531.             #Operaattorina AND koska muuten ehto toteutuisi myös klo. 22 jälkeisiltä tunneilta
  532.             logi("PÄIVÄSÄHKÖN TUNTI")
  533.             data['sahko']['pulssit_paiva'] += data['piirturi_pulssit']  #Summataan Päivätunnin pulssit päivätariffille
  534.            
  535.             if data['vesi']['varaajan_pakotus'] == False:       #Jos päällepakotus EI ole päällä
  536.                 GPIO.output(25, 0)                              #Lähdön tila = 0, varaajan sammuu
  537.                 logi("Varaaja = kiinni")
  538.             else:                                               #Jos halutaan tehdä jotain tässäkohtaa ...
  539.                 logi("Varaaja on pakotettuna päälle päivällä! Ei tehty mitään!")
  540.  
  541.         ##################
  542.         logi("TUNTI nro. " + str(tunti) + "vaihtunut!")
  543.         logi("Varaajan kärkitila: " + str(GPIO.input(25)))
  544.         data['piirturi_pulssit'] = 0                            #Nollataan pulssilaskuri uutta tuntia varten
  545.         data['piirturi_count'] = 0                              #Varmistetaan ettei tätä tasatunnin vaihtumista suoriteta heti uudestaan, vaikka eletään samaa tasaminuuttia
  546.  
  547.    
  548.     data['piirturi_count'] += 1            
  549.     #Lisätään joka kierroksella 1, jotta tasatunnin vaihtumista ei kutsuta uudestaan, vaikka minuutti pysyy samana
  550.    
  551.     data['sahko']['realTimeMonitorLog'].append(teho)
  552.  
  553.        
  554.     ####### ILPin hetkellinen teho #############
  555.     if data['ilp']['ilp_delay'] > 0:
  556.         data['ilp']['ilp_teho'] = round((((1000/1000)*3600)/data['ilp']['ilp_delay'])/1000, 3)
  557.    
  558. def vesimittari(channel): #Keskeytyshaaskat kutsuu aina tämän paikalle, jos GPIO nastoissa tapahtuu jotain
  559.     global data           #Keskeytyksissä summataan pulssit SEKÄ lasketaan taajuus
  560.     if channel == 17:
  561.         data['sahko']['pulssien_vali'] = time.time() - data['sahko']['lastTimestamp']
  562.         data['kumul_pulssit'] += 1
  563.         data['piirturi_pulssit'] += 1
  564.         data['sahko']['lastTimestamp'] = time.time()
  565.         return 0
  566.     if channel == 26:
  567.         data['vesi']['taajuus'] = 1 / (time.time() - data['vesi']['lastTimestamp'])
  568.         data['vesi']['lastTimestamp'] = time.time()
  569.         data['vesi']['ulkokierto_count'] += 1
  570.         return 0
  571.     if channel == 13:
  572.         data['vesi']['kylma_vesi_taajuus'] = 1 / (time.time() - data['vesi']['kylma_vesi_lastTimestamp'])
  573.         data['vesi']['kylma_vesi_lastTimestamp'] = time.time()
  574.         data['vesi']['kylma_vesi_count'] += 1
  575.         return 0
  576.     if channel == 27:
  577.         data['ilp']['ilp_delay'] = time.time() - data['ilp']['lastTimestamp']
  578.         data['ilp']['ilp_count'] += 1
  579.         data['ilp']['lastTimestamp'] = time.time()
  580.        
  581.  
  582. def pysayta_mittari(): #Kun virtaus pysähtyy, ei keskeytystä enään tule == ei jätetä edellistä taajuuslukemaa päälle
  583.     global data
  584.     if time.time() - data['vesi']['lastTimestamp'] > 1:
  585.         data['vesi']['taajuus'] = 0
  586.     if time.time() - data['vesi']['kylma_vesi_lastTimestamp'] > 1:
  587.         data['vesi']['kylma_vesi_taajuus'] = 0
  588.     if time.time() - data['ilp']['lastTimestamp'] > 360:
  589.         data['ilp']['lastTimestamp'] = time.time()
  590.         logi("Nollattiin ILPin mittari")
  591.         data['ilp']['ilp_teho'] = 0
  592.    
  593.     #if time.time() - data['sahko']['lastTimestamp'] > 1:
  594.     #    data['sahko']['pulssien_vali'] = False
  595.        
  596.        
  597. def mittaa_lampotila():
  598.     global f
  599.     val_2 = ADS.readADC(0)
  600.     val_1 = ADS.readADC(2)
  601.  
  602.     jannite = val_2 * f
  603.     vastus = round(47000 / ((val_1 * f) / jannite -1), 2)
  604.     #print("Analog2: {0:d}\t{1:.3f} V".format(val_2, jannite))
  605.     #B VALUE = 3950
  606.     lampo = (1 / (1 / 298.16 +1 / 3950 * ln(vastus / 50000))) - 273.15
  607.  
  608.     #print(lampo , "vastus: " , vastus, "3.3 jannite: ", val_1 * f)
  609.     return lampo
  610.  
  611.    
  612. def logi(txt):
  613.     print("[", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), " ] ", txt)
  614.     data['lastLogs'].append("[" + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "] "  + txt + "")
  615.     time.sleep(1)
  616.  
  617.  
  618. class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
  619.     logi("Threaded Mixin luokkaa kutsutaan!")
  620.     """Handle requests in a separate thread."""
  621.  
  622. def create_server():
  623.     port = 3323
  624.     global data, vesi
  625.     httpd = ThreadedHTTPServer(("0.0.0.0", port), handler)
  626.     logi("HTTP KUUNTELEE PORTISSA " + str(port))
  627.     httpd.serve_forever()
  628.  
  629. try: #Jos data array on aiemmin dumpattu tiedostoon talteen
  630.     with open("demofile3.txt", "r") as file:
  631.         logi("TIEDOSTO LÖYTYI")
  632.         data = json.loads(file.read())
  633.         file.close()
  634. except:
  635.     logi("Ei tiedostoa")
  636.  
  637.  
  638. #ADS = ADS1x15.ADS1115(1, 0x48)
  639. #ADS.setGain(ADS.PGA_4_096V)
  640. #f = ADS.toVoltage()
  641. #ADS.setDataRate(0)
  642.  
  643.  
  644. #Haaskat odottamassa:
  645. benchmark = False
  646.  
  647. if benchmark == False:
  648.     GPIO.setmode(GPIO.BCM)
  649.    
  650.     GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)     #Kuuma vesi
  651.     GPIO.add_event_detect(26, GPIO.FALLING, bouncetime=2)
  652.     GPIO.add_event_callback(26, vesimittari)
  653.  
  654.     GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)     #Kylmä vesi
  655.     GPIO.add_event_detect(13, GPIO.FALLING, bouncetime=2)
  656.     GPIO.add_event_callback(13, vesimittari)
  657.  
  658.  
  659.     GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)     #Sähkömittari
  660.     GPIO.add_event_detect(17, GPIO.FALLING, bouncetime=2)
  661.     GPIO.add_event_callback(17, vesimittari)
  662.    
  663.     GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)     # ILP sähkömittari
  664.     GPIO.add_event_detect(27, GPIO.FALLING, bouncetime=30)
  665.     GPIO.add_event_callback(27, vesimittari)
  666.  
  667.     #GPIO.setup(21, GPIO.OUT, initial=GPIO.LOW)
  668.     #GPIO.setup(20, GPIO.OUT, initial=GPIO.LOW)
  669.     GPIO.setup(25, GPIO.OUT, initial=GPIO.LOW)
  670.     #Varaajan kärki. Avautuva kosketin = oletuksena varaajan kontaktorille avautuva kosketin, varaaja silloin päällä
  671.  
  672.  
  673.  
  674.  
  675.  
  676. for i in range(5):
  677.     time.sleep(1)
  678.     logi("odotetaan " + str(i))
  679.  
  680. #Kodin hengetär tekee ns. raskaat, aikaa vievät työt ja paskahommat
  681. #Sekunttikierto laskee veden kulutukset ja sen keston täytyy olla tarkasti sekuntti
  682.  
  683.  
  684. time.sleep(1)
  685.  
  686. _thread.start_new_thread(kodin_hengetar, tuple())
  687. logi("Kodin hengerät käynnistetty!")
  688. hearthbeat()
  689. logi("hearthbeat() käynnistetty!")
  690.  
  691. time.sleep(1)
  692.  
  693.  
  694. _thread.start_new_thread(create_server, tuple())
  695.  
  696. data['meta']['restartTimes'] += 1
  697. kaynnistysaika = math.ceil(time.time())
  698.  
  699. try:
  700.     start = time.time()
  701.     while True:             #Mainloop ei tee muuta kuin odottaa pysäytyskäskyä näppikseltä
  702.         #print(data["vesi"]["ulkokierto_count"])
  703.         #print(data["sahko"]["pulssien_vali"])
  704.         #time.sleep(1 / 183.333)
  705.         #vesimittari(26)
  706.         #print(time.time() - start)
  707.         time.sleep(2)
  708. except KeyboardInterrupt:
  709.     logi("tallennetaan data")
  710.     file = open("demofile3.txt", "w")
  711.     file.write(json.dumps(data))
  712.     file.close()
  713.     data['meta']['ajoaikaSec'] += (math.ceil(time.time()) - kaynnistysaika)
  714.    
  715.     print("\nTidying up")
  716.  
captcha