311 lines
8.3 KiB
C
311 lines
8.3 KiB
C
/****************************************************************************
|
|
* This file is part of the project AqHome.
|
|
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
|
|
*
|
|
* The license for this file can be found in the file COPYING which you
|
|
* should have received along with this file.
|
|
****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include "./u_suntime_p.h"
|
|
#include "aqhome-react/suntimes.h"
|
|
|
|
#include <gwenhywfar/debug.h>
|
|
#include <gwenhywfar/xml.h>
|
|
#include <gwenhywfar/text.h>
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* defines
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#define AQHOMEREACT_UNIT_SUNTIME_OUTSLOT_OUTPUT 0
|
|
#define AQHOMEREACT_UNIT_SUNTIME_INSLOT_TIMER 0
|
|
|
|
GWEN_INHERIT(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME);
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* forward declarations
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
|
|
static int _cbProcess(AQHREACT_UNIT *unit);
|
|
static int _checkState(AQHREACT_UNIT *unit);
|
|
static int _isInsideSuntime(AQHREACT_UNIT *unit);
|
|
static void _updateSuntimes(AQHREACT_UNIT *unit);
|
|
static int _hasDateChanged(AQHREACT_UNIT *unit);
|
|
static int _gwenTimeToMinutes(const GWEN_TIME *t);
|
|
static int _outputResult(AQHREACT_UNIT *unit, int result);
|
|
static void _readParams(AQHREACT_UNIT *unit);
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* implementations
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
AQHREACT_UNIT *AqHomeReact_UnitSuntime_new(AQH_OBJECT *aqh)
|
|
{
|
|
AQHREACT_UNIT *unit;
|
|
AQHREACT_UNIT_SUNTIME *xunit;
|
|
AQHREACT_PORT *port;
|
|
AQHREACT_PARAM *param;
|
|
|
|
unit=AQHREACT_Unit_new(aqh);
|
|
AQHREACT_Unit_SetTypeName(unit, "suntime");
|
|
AQHREACT_Unit_SetDescription(unit, "Make sunset and sunrise times available");
|
|
AQHREACT_Unit_SetGpInt(unit, -1);
|
|
|
|
GWEN_NEW_OBJECT(AQHREACT_UNIT_SUNTIME, xunit);
|
|
GWEN_INHERIT_SETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit, xunit, _freeData);
|
|
|
|
AQHREACT_Unit_SetProcessFn(unit, _cbProcess);
|
|
|
|
port=AQHREACT_Port_new();
|
|
AQHREACT_Port_SetName(port, "output");
|
|
AQHREACT_Port_SetIdForUnit(port, AQHOMEREACT_UNIT_SUNTIME_OUTSLOT_OUTPUT);
|
|
AQHREACT_Port_SetDataType(port, AQHREACT_DATAOBJECTTYPE_DOUBLE);
|
|
AQHREACT_Unit_AddOutputPort(unit, port);
|
|
|
|
port=AQHREACT_Port_new();
|
|
AQHREACT_Port_SetName(port, "timer");
|
|
AQHREACT_Port_SetIdForUnit(port, AQHOMEREACT_UNIT_SUNTIME_INSLOT_TIMER);
|
|
AQHREACT_Port_SetDataType(port, AQHREACT_DATAOBJECTTYPE_DOUBLE);
|
|
AQHREACT_Unit_AddInputPort(unit, port);
|
|
|
|
param=AQHREACT_Param_new();
|
|
AQHREACT_Param_SetName(param, AQHOMEREACT_UNIT_SUNTIME_PARAM_LAT);
|
|
AQHREACT_Param_SetDataType(param, AQHREACT_DATAOBJECTTYPE_DOUBLE);
|
|
AQHREACT_Unit_AddParam(unit, param);
|
|
|
|
param=AQHREACT_Param_new();
|
|
AQHREACT_Param_SetName(param, AQHOMEREACT_UNIT_SUNTIME_PARAM_LONG);
|
|
AQHREACT_Param_SetDataType(param, AQHREACT_DATAOBJECTTYPE_DOUBLE);
|
|
AQHREACT_Unit_AddParam(unit, param);
|
|
|
|
param=AQHREACT_Param_new();
|
|
AQHREACT_Param_SetName(param, AQHOMEREACT_UNIT_SUNTIME_PARAM_OFFS_RISE);
|
|
AQHREACT_Param_SetDataType(param, AQHREACT_DATAOBJECTTYPE_DOUBLE);
|
|
AQHREACT_Unit_AddParam(unit, param);
|
|
|
|
param=AQHREACT_Param_new();
|
|
AQHREACT_Param_SetName(param, AQHOMEREACT_UNIT_SUNTIME_PARAM_OFFS_SET);
|
|
AQHREACT_Param_SetDataType(param, AQHREACT_DATAOBJECTTYPE_DOUBLE);
|
|
AQHREACT_Unit_AddParam(unit, param);
|
|
|
|
return unit;
|
|
|
|
}
|
|
|
|
|
|
|
|
void _freeData(GWEN_UNUSED void *bp, void *p)
|
|
{
|
|
AQHREACT_UNIT_SUNTIME *xunit;
|
|
|
|
xunit=(AQHREACT_UNIT_SUNTIME*) p;
|
|
|
|
GWEN_Date_free(xunit->date);
|
|
GWEN_FREE_OBJECT(xunit);
|
|
}
|
|
|
|
|
|
|
|
int _cbProcess(AQHREACT_UNIT *unit)
|
|
{
|
|
AQHREACT_UNIT_SUNTIME *xunit;
|
|
|
|
xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit);
|
|
if (xunit) {
|
|
if (unit /* && AQHREACT_Unit_InputHasChanged(unit)*/) {
|
|
int rv;
|
|
|
|
if (AQHREACT_Unit_GetGpInt(unit)==-1)
|
|
_readParams(unit);
|
|
|
|
rv=_checkState(unit);
|
|
AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit);
|
|
return rv;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int _checkState(AQHREACT_UNIT *unit)
|
|
{
|
|
AQHREACT_UNIT_SUNTIME *xunit;
|
|
|
|
xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit);
|
|
if (xunit) {
|
|
time_t now;
|
|
|
|
now=time(NULL);
|
|
if (60.0<difftime(now, xunit->lastCheckTime)) {
|
|
if (_hasDateChanged(unit))
|
|
_updateSuntimes(unit);
|
|
xunit->lastCheckTime=now;
|
|
return _outputResult(unit, _isInsideSuntime(unit));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int _isInsideSuntime(AQHREACT_UNIT *unit)
|
|
{
|
|
AQHREACT_UNIT_SUNTIME *xunit;
|
|
|
|
xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit);
|
|
if (xunit) {
|
|
GWEN_TIME *now;
|
|
int startTimeInMinutes;
|
|
int endTimeInMinutes;
|
|
int nowInMinutes;
|
|
int result;
|
|
|
|
now=GWEN_CurrentTime();
|
|
nowInMinutes=_gwenTimeToMinutes(now);
|
|
GWEN_Time_free(now);
|
|
|
|
startTimeInMinutes=xunit->sunRiseTimeInMinutes+xunit->offsetMinsForSunrise;
|
|
endTimeInMinutes=xunit->sunSetTimeInMinutes+xunit->offsetMinsForSunset;
|
|
|
|
result=(nowInMinutes>=startTimeInMinutes && nowInMinutes<=endTimeInMinutes)?1:0;
|
|
DBG_INFO(NULL, "Is inside suntime: %d", result);
|
|
return result;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void _updateSuntimes(AQHREACT_UNIT *unit)
|
|
{
|
|
AQHREACT_UNIT_SUNTIME *xunit;
|
|
|
|
xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit);
|
|
if (xunit) {
|
|
GWEN_TIME *t;
|
|
|
|
GWEN_Date_free(xunit->date);
|
|
xunit->date=GWEN_Date_CurrentDate();
|
|
|
|
t=AQHomeReact_GetSunriseTimeForDateAndLoc(xunit->date, xunit->latitude, xunit->longitude);
|
|
xunit->sunRiseTimeInMinutes=_gwenTimeToMinutes(t);
|
|
DBG_INFO(NULL, "%s: Sunrise today at %02d:%02d UTC",
|
|
GWEN_Date_GetString(xunit->date), xunit->sunRiseTimeInMinutes/60, xunit->sunRiseTimeInMinutes%60);
|
|
GWEN_Time_free(t);
|
|
|
|
t=AQHomeReact_GetSunsetTimeForDateAndLoc(xunit->date, xunit->latitude, xunit->longitude);
|
|
xunit->sunSetTimeInMinutes=_gwenTimeToMinutes(t);
|
|
DBG_INFO(NULL, "%s: Sunset today at %02d:%02d UTC",
|
|
GWEN_Date_GetString(xunit->date), xunit->sunSetTimeInMinutes/60, xunit->sunSetTimeInMinutes%60);
|
|
GWEN_Time_free(t);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int _gwenTimeToMinutes(const GWEN_TIME *t)
|
|
{
|
|
if (t) {
|
|
int hours;
|
|
int mins;
|
|
int secs;
|
|
|
|
GWEN_Time_GetBrokenDownUtcTime(t, &hours, &mins, &secs);
|
|
return (hours*60)+mins;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int _hasDateChanged(AQHREACT_UNIT *unit)
|
|
{
|
|
AQHREACT_UNIT_SUNTIME *xunit;
|
|
|
|
xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit);
|
|
if (xunit) {
|
|
if (xunit->date==NULL)
|
|
return 1;
|
|
else {
|
|
GWEN_DATE *today;
|
|
|
|
today=GWEN_Date_CurrentDate();
|
|
|
|
if (GWEN_Date_Compare(today, xunit->date)!=0) {
|
|
GWEN_Date_free(today);
|
|
return 1;
|
|
}
|
|
GWEN_Date_free(today);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void _readParams(AQHREACT_UNIT *unit)
|
|
{
|
|
AQHREACT_UNIT_SUNTIME *xunit;
|
|
|
|
xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit);
|
|
if (xunit) {
|
|
DBG_INFO(NULL, "Reading parameters");
|
|
/* default to "Neues Rathaus" Celle ;-) */
|
|
xunit->latitude=AQHREACT_Unit_GetParamValueDouble(unit, AQHOMEREACT_UNIT_SUNTIME_PARAM_LAT, 52.619425);
|
|
xunit->longitude=AQHREACT_Unit_GetParamValueDouble(unit, AQHOMEREACT_UNIT_SUNTIME_PARAM_LONG, 10.087891);
|
|
xunit->offsetMinsForSunrise=AQHREACT_Unit_GetParamValueDouble(unit, AQHOMEREACT_UNIT_SUNTIME_PARAM_OFFS_RISE, 0.0);
|
|
xunit->offsetMinsForSunset=AQHREACT_Unit_GetParamValueDouble(unit, AQHOMEREACT_UNIT_SUNTIME_PARAM_OFFS_SET, 0.0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int _outputResult(AQHREACT_UNIT *unit, int result)
|
|
{
|
|
AQHREACT_PORT *port;
|
|
|
|
port=AQHREACT_Unit_GetOutputPortByIdForUnit(unit, AQHOMEREACT_UNIT_SUNTIME_OUTSLOT_OUTPUT);
|
|
if (port) {
|
|
int currentState;
|
|
|
|
currentState=AQHREACT_Unit_GetGpInt(unit);
|
|
if (AQHREACT_Unit_GetFlags(unit) & AQHREACT_UNIT_FLAGS_INVERT)
|
|
result^=1;
|
|
if (currentState!=result) {
|
|
DBG_INFO(NULL, "%s: Changing result to %d", AQHREACT_Unit_GetId(unit), result);
|
|
AQHREACT_Unit_OutputDoubleData(unit, port, result?1.0:0.0);
|
|
AQHREACT_Unit_SetGpInt(unit, result);
|
|
return 1; /* we changed something */
|
|
}
|
|
}
|
|
return 0; /* nothing changed */
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|