/**************************************************************************** * 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 #endif #include "./u_suntime_p.h" #include "aqhome-react/suntimes.h" #include #include #include /* ------------------------------------------------------------------------------------------------ * 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(AQHOME_REACT *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.0lastCheckTime)) { 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_DEBUG(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) return 1; } } 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 */ }