diff --git a/interface/src/project/GeneralInformation.tsx b/interface/src/project/GeneralInformation.tsx index 91c976d..c161693 100644 --- a/interface/src/project/GeneralInformation.tsx +++ b/interface/src/project/GeneralInformation.tsx @@ -4,6 +4,7 @@ import {FormButton, restController, RestControllerProps, RestFormLoader, Section import {ENDPOINT_ROOT} from "../api"; import {GeneralInformaitonState} from "./types"; import RefreshIcon from "@material-ui/icons/Refresh"; +import {getRealTimeString, stringifyTime} from "./Time"; // define api endpoint export const GENERALINFORMATION_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "generalinfo"; @@ -11,16 +12,10 @@ export const GENERALINFORMATION_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "generalinfo type GeneralInformationRestControllerProps = RestControllerProps; class GeneralInformation extends Component { - intervalhandler: number | undefined; - componentDidMount() { this.props.loadData(); } - componentWillUnmount() { - clearInterval(this.intervalhandler); - } - render() { return ( @@ -32,35 +27,34 @@ class GeneralInformation extends Component {stringifyTime(props.data.runtime)} +
{getRealTimeString(props.data.runtime)}} /> + vor {stringifyTime(props.data.lastWaterOutage)}
+ {getRealTimeString(props.data.lastWaterOutage)} + : "noch nie!"} />
+ vor {stringifyTime(props.data.lastpumptime)}
+ {getRealTimeString(props.data.lastpumptime)} + : "noch nie!"} />
- - - @@ -87,40 +81,6 @@ class GeneralInformation extends Component ); } - - /** - * stringify seconds to a pretty format - * @param sec number of seconds - */ - private stringifyTime(sec: number): string { - if (sec >= 86400) { - // display days - return (Math.trunc(sec / 86400) + "d " + Math.trunc((sec % 86400) / 3600) + "h " + Math.trunc((sec % 3600) / 60) + "min " + sec % 60 + "sec"); - } else if (sec >= 3600) { - // display hours - return (Math.trunc(sec / 3600) + "h " + Math.trunc((sec % 3600) / 60) + "min " + sec % 60 + "sec"); - } else if (sec >= 60) { - // only seconds and minutes - return (Math.trunc(sec / 60) + "min " + sec % 60 + "sec"); - } else { - // only seconds - return (sec + "sec"); - } - } - - private getRealTimeString(runtime: number): string { - const timestamp = Date.now(); - - const a = new Date(timestamp - (runtime * 1000)); - const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; - const year = a.getFullYear(); - const month = months[a.getMonth()]; - const date = a.getDate(); - const hour = a.getHours(); - const min = "0" + a.getMinutes(); - const sec = "0" + a.getSeconds(); - return '[' + date + ' ' + month + ' ' + year + ' ' + hour + ':' + min.substr(-2) + ':' + sec.substr(-2) + ']'; - } } export default restController(GENERALINFORMATION_SETTINGS_ENDPOINT, GeneralInformation); diff --git a/interface/src/project/HeatingInformation.tsx b/interface/src/project/HeatingInformation.tsx new file mode 100644 index 0000000..5772138 --- /dev/null +++ b/interface/src/project/HeatingInformation.tsx @@ -0,0 +1,64 @@ +import React from "react"; +import {FormButton, restController, RestControllerProps, RestFormLoader, SectionContent} from "../components"; +import {ENDPOINT_ROOT} from "../api"; +import {Box, List, ListItem, ListItemText} from "@material-ui/core"; +import RefreshIcon from "@material-ui/icons/Refresh"; +import {getRealTimeString, stringifyTime} from "./Time"; +import {HeatingInformationState} from "./types"; + +// define api endpoint +export const HEATING_ENDPOINT = ENDPOINT_ROOT + "heatinginfo"; + +type HeatingRestcontollerprops = RestControllerProps; + +class HeatingInformation extends React.Component { + componentDidMount() { + this.props.loadData(); + } + + render = () => ( + + ( + <> + + + + vor {stringifyTime(props.data.lastheating)}
+ {getRealTimeString(props.data.lastheating)} + : "noch nie!"} + /> +
+ + + + + + +
+ + + } variant="contained" color="secondary" + onClick={this.props.loadData}> + Refresh + + + + + )} + /> +
+ ); +} + +export default restController(HEATING_ENDPOINT, HeatingInformation); \ No newline at end of file diff --git a/interface/src/project/PumpControl.tsx b/interface/src/project/PumpControl.tsx index 204e812..74abc34 100644 --- a/interface/src/project/PumpControl.tsx +++ b/interface/src/project/PumpControl.tsx @@ -9,6 +9,7 @@ import { AuthenticatedRoute } from '../authentication'; import GeneralInformation from './GeneralInformation'; import SettingsController from "./SettingsController"; +import HeatingInformation from "./HeatingInformation"; class PumpControl extends Component { @@ -21,10 +22,12 @@ class PumpControl extends Component { + + diff --git a/interface/src/project/Time.ts b/interface/src/project/Time.ts new file mode 100644 index 0000000..51a6440 --- /dev/null +++ b/interface/src/project/Time.ts @@ -0,0 +1,33 @@ +/** + * stringify seconds to a pretty format + * @param sec number of seconds + */ +export function stringifyTime(sec: number): string { + if (sec >= 86400) { + // display days + return (Math.trunc(sec / 86400) + "d " + Math.trunc((sec % 86400) / 3600) + "h " + Math.trunc((sec % 3600) / 60) + "min " + sec % 60 + "sec"); + } else if (sec >= 3600) { + // display hours + return (Math.trunc(sec / 3600) + "h " + Math.trunc((sec % 3600) / 60) + "min " + sec % 60 + "sec"); + } else if (sec >= 60) { + // only seconds and minutes + return (Math.trunc(sec / 60) + "min " + sec % 60 + "sec"); + } else { + // only seconds + return (sec + "sec"); + } +} + +export function getRealTimeString(runtime: number): string { + const timestamp = Date.now(); + + const a = new Date(timestamp - (runtime * 1000)); + const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + const year = a.getFullYear(); + const month = months[a.getMonth()]; + const date = a.getDate(); + const hour = a.getHours(); + const min = "0" + a.getMinutes(); + const sec = "0" + a.getSeconds(); + return '[' + date + ' ' + month + ' ' + year + ' ' + hour + ':' + min.substr(-2) + ':' + sec.substr(-2) + ']'; +} \ No newline at end of file diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index 460dd62..5153a22 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -7,8 +7,6 @@ export interface SettingsState { } export interface GeneralInformaitonState { - hum: number; - temp: number; lastpumptime: number; lastWaterOutage: number; lastPumpDuration: number; @@ -17,3 +15,11 @@ export interface GeneralInformaitonState { pressuresensor: boolean; version: string; } + +export interface HeatingInformationState { + lastheating: number; + lastheatduration: number; + hum: number; + temp: number; + +} diff --git a/src/GeneralInfoService.cpp b/src/GeneralInfoService.cpp index 7062e40..f20a9d0 100644 --- a/src/GeneralInfoService.cpp +++ b/src/GeneralInfoService.cpp @@ -6,9 +6,7 @@ #include "Pins.h" #include "Timer.h" -GeneralInfoService::GeneralInfoService(AsyncWebServer *server, float *lasthum, float *lasttemp) : - hum(lasthum), - temp(lasttemp), +GeneralInfoService::GeneralInfoService(AsyncWebServer *server) : lastPumpTime(0), lastWaterOutage(0), lastPumpDuration(0) { @@ -19,9 +17,6 @@ void GeneralInfoService::reply(AsyncWebServerRequest *request) { AsyncJsonResponse *response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE); JsonObject root = response->getRoot(); - root["temp"] = *temp; - root["hum"] = *hum; - const uint32_t systime = Timer::getSystemSeconds(); root["lastpumptime"] = lastPumpTime != 0 ? systime - lastPumpTime : 0; diff --git a/src/GeneralInfoService.h b/src/GeneralInfoService.h index b2947d1..371234f 100644 --- a/src/GeneralInfoService.h +++ b/src/GeneralInfoService.h @@ -17,7 +17,7 @@ class GeneralInfoService { public: - GeneralInfoService(AsyncWebServer* server, float* lasthum, float* lasttemp); + GeneralInfoService(AsyncWebServer* server); /** * sets the last time when the pump was on @@ -40,9 +40,6 @@ public: private: void reply(AsyncWebServerRequest* request); - float * hum; - float * temp; - unsigned long lastPumpTime, lastWaterOutage, lastPumpDuration; }; diff --git a/src/Heating.cpp b/src/Heating.cpp index fa0f1af..9d8e300 100644 --- a/src/Heating.cpp +++ b/src/Heating.cpp @@ -4,18 +4,21 @@ #include "Heating.h" #include "Pins.h" +#include "Timer.h" Heating::Heating() : mHeizungTicker(), mLuefterTicker(), mTurnOffTicker(), msettings(nullptr), - mHeatingStatus(), + mheatingservice(nullptr), + mHeatingStatus(false), lasttemp(0), lasthum(0), sensor() {} -void Heating::init(unsigned mode, const SettingState *settings) { +void Heating::init(unsigned mode, const SettingState *settings, HeatingInfoService *heatingservice) { msettings = settings; + mheatingservice = heatingservice; switch (mode) { case TIME: { @@ -27,11 +30,13 @@ void Heating::init(unsigned mode, const SettingState *settings) { sensor.setPin(TempSensorPin); - sensor.onData([this, settings](float hum, float temp) { - schedule_function([hum, temp, settings, this]() { + sensor.onData([this](float hum, float temp) { + schedule_function([hum, temp, this]() { Serial.printf("Temp: %gdegC\n", temp); Serial.printf("Humid: %g%%\n", hum); + mheatingservice->setSensorData(hum, temp); + this->lasttemp = temp; this->lasthum = hum; @@ -43,8 +48,10 @@ void Heating::init(unsigned mode, const SettingState *settings) { this->lasttemp = -1.0; this->lasthum = -1.0; + mheatingservice->setSensorData(-1.0, -1.0); + // emergency turnoff of heating - if(mHeatingStatus){ + if (mHeatingStatus) { digitalWrite(HeizungPin, LOW); } @@ -63,39 +70,41 @@ void Heating::init(unsigned mode, const SettingState *settings) { } } -float *Heating::getLastTemp() { - return &lasttemp; -} - -float *Heating::getLastHum() { - return &lasthum; -} - void Heating::handleHeatingEvents() { if (this->lasthum > (float) msettings->heatUp) { - // turn off active turnoff timers - mLuefterTicker.detach(); - Serial.println("heating should run now!"); - // turn on heating and fan - digitalWrite(LuefterPin, HIGH); - digitalWrite(HeizungPin, HIGH); - mHeatingStatus = true; + // only turn on if turned off + if (mHeatingStatus == false) { + // turn off active turnoff timers + mLuefterTicker.detach(); + Serial.println("heating should run now!"); + // turn on heating and fan + digitalWrite(LuefterPin, HIGH); + digitalWrite(HeizungPin, HIGH); + + mHeatingStatus = true; + mheatingservice->setLastHeatingStarttime(Timer::getSystemSeconds()); + + mheatingservice->setLastHetingEndtime(0); + } } else if (this->lasthum < (float) msettings->heatLow) { // if humidity too low turn off heating and fan after 60secs - digitalWrite(HeizungPin, LOW); - Serial.println("heating should NOT run now!"); + if (mHeatingStatus == true) { + digitalWrite(HeizungPin, LOW); + Serial.println("heating should NOT run now!"); - // if heating status in on set ticker to turn of fan in 60sec - if (mHeatingStatus) { - mLuefterTicker.once((float) msettings->fanRuntime, []() { - // turn off fan - digitalWrite(LuefterPin, LOW); - schedule_function([]() { - Serial.println("turning off fan"); + // if heating status in on set ticker to turn of fan in 60sec + if (mHeatingStatus) { + mLuefterTicker.once((float) msettings->fanRuntime, []() { + // turn off fan + digitalWrite(LuefterPin, LOW); + schedule_function([]() { + Serial.println("turning off fan"); + }); }); - }); + } + mheatingservice->setLastHetingEndtime(Timer::getSystemSeconds()); + mHeatingStatus = false; } - mHeatingStatus = false; } } diff --git a/src/Heating.h b/src/Heating.h index d238b3a..d4b5980 100644 --- a/src/Heating.h +++ b/src/Heating.h @@ -8,6 +8,7 @@ #include #include "Pins.h" #include "SettingsService.h" +#include "HeatingInfoService.h" class Heating { public: @@ -18,19 +19,7 @@ public: * @param mode select the operating mode * @param settings a settings object is needed for HUMIDITY mode to get percent values set by user */ - void init(unsigned mode, const SettingState* settings); - - /** - * get the last captured humidity value - * @return humidity value - */ - float* getLastHum(); - - /** - * get the last captured temperature value - * @return temperature value - */ - float* getLastTemp(); + void init(unsigned mode, const SettingState* settings, HeatingInfoService *heatingservice); enum MODES {TIME, HUMIDITY}; private: @@ -39,6 +28,7 @@ private: Ticker mTurnOffTicker; const SettingState* msettings; + HeatingInfoService *mheatingservice; bool mHeatingStatus; diff --git a/src/HeatingInfoService.cpp b/src/HeatingInfoService.cpp new file mode 100644 index 0000000..35780be --- /dev/null +++ b/src/HeatingInfoService.cpp @@ -0,0 +1,46 @@ +// +// Created by lukas on 19.12.20. +// + +#include +#include "HeatingInfoService.h" +#include "Timer.h" + +HeatingInfoService::HeatingInfoService(AsyncWebServer *server) : + hum(-1), + temp(-1), + lastheating(0), + lastheatend(0) { + server->on(HEATINGINFO_SERVICE_PATH, HTTP_GET, std::bind(&HeatingInfoService::reply, this, std::placeholders::_1)); +} + +void HeatingInfoService::reply(AsyncWebServerRequest *request) { + AsyncJsonResponse *response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE); + JsonObject root = response->getRoot(); + + root["temp"] = temp; + root["hum"] = hum; + + const uint32_t systime = Timer::getSystemSeconds(); + + root["lastheating"] = lastheating != 0 ? systime - lastheating : 0; + // if lastheating == 0 => there was no cycle upon now + // if lastheatend == 0 => heating is currently active + root["lastheatduration"] = lastheating == 0 ? 0 : lastheatend == 0 ? -1 : lastheatend - lastheating; + + response->setLength(); + request->send(response); +} + +void HeatingInfoService::setSensorData(float hum, float temp) { + this->hum = hum; + this->temp = temp; +} + +void HeatingInfoService::setLastHeatingStarttime(unsigned long time) { + this->lastheating = time; +} + +void HeatingInfoService::setLastHetingEndtime(unsigned long time) { + this->lastheatend = time; +} diff --git a/src/HeatingInfoService.h b/src/HeatingInfoService.h new file mode 100644 index 0000000..76f1be9 --- /dev/null +++ b/src/HeatingInfoService.h @@ -0,0 +1,35 @@ +// +// Created by lukas on 19.12.20. +// + +#ifndef PUMPENSTEUERUNG_HEATINGINFOSERVICE_H +#define PUMPENSTEUERUNG_HEATINGINFOSERVICE_H + +#include + +#define MAX_FEATURES_SIZE 256 +#define HEATINGINFO_SERVICE_PATH "/rest/heatinginfo" // set the api backend path + + +class HeatingInfoService { +public: + HeatingInfoService(AsyncWebServer* server); + + void setSensorData(float hum, float temp); + + void setLastHeatingStarttime(unsigned long time); + + void setLastHetingEndtime(unsigned long time); + +private: + void reply(AsyncWebServerRequest* request); + + float hum; + float temp; + + unsigned long lastheating; + unsigned long lastheatend; +}; + + +#endif //PUMPENSTEUERUNG_HEATINGINFOSERVICE_H diff --git a/src/Pins.h b/src/Pins.h index 8614ecb..08732a7 100644 --- a/src/Pins.h +++ b/src/Pins.h @@ -16,6 +16,6 @@ #define TempSensorPin D4 // version info -#define VERSION "v1.2.2" +#define VERSION "v1.2.3" #endif //PUMPENSTEUERUNG_PINS_H diff --git a/src/main.cpp b/src/main.cpp index e40605a..eb23aaf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "GeneralInfoService.h" #include "SettingsService.h" #include "Timer.h" +#include "HeatingInfoService.h" bool allow; bool error = false; @@ -24,7 +25,8 @@ uint32_t turnontime = 0; AsyncWebServer server(80); ESP8266React esp8266React(&server); -GeneralInfoService generalinfo = GeneralInfoService(&server, mHeat.getLastHum(), mHeat.getLastTemp()); +GeneralInfoService generalinfo = GeneralInfoService(&server); +HeatingInfoService heatinginfo = HeatingInfoService(&server); SettingsService settingsservice = SettingsService(&server, esp8266React.getSecurityManager()); void pumpeSchalten(bool on) { @@ -180,7 +182,7 @@ void setup() { // initialize heating control Serial.println("initializing heating service"); - mHeat.init(Heating::HUMIDITY, settingsservice.getSettings()); + mHeat.init(Heating::HUMIDITY, settingsservice.getSettings(), &heatinginfo); Serial.println("startup sequence complete!\n"); }