From b30e17b151fc68f5b58d603c4092fb2d687592ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ka=C5=82wak?= Date: Fri, 29 May 2026 11:28:11 +0200 Subject: [PATCH] General purpose text channel support Fixes for builder --- ...yedGeneralPurposeTextValueBasedTrigger.sql | 58 ++++ sql/AddGeneralPurposeText.sql | 23 ++ ...AddGeneralPurposeTextValueBasedTrigger.sql | 81 ++++++ sql/TSDB_AddGeneralPurposeText.sql | 11 + sql/TSDB_TestDatabaseStructureAndData.sql | 3 + sql/TestDatabaseStructureAndData.sql | 41 +++ supla-client/Android/jni/Android.mk | 1 + supla-client/Android/jni/channel_config.cpp | 9 + .../channel_config_general_purpose_text.cpp | 42 +++ .../jni/channel_config_general_purpose_text.h | 37 +++ supla-common/proto.h | 15 +- supla-server/Debug/src/datalogger/subdir.mk | 6 + .../Debug/src/device/extended_value/subdir.mk | 3 + supla-server/Debug/src/device/value/subdir.mk | 5 +- supla-server/Debug/src/ipc/subdir.mk | 6 + supla-server/Release/src/datalogger/subdir.mk | 6 + .../src/device/extended_value/subdir.mk | 3 + .../Release/src/device/value/subdir.mk | 3 + supla-server/Release/src/ipc/subdir.mk | 6 + .../Release/src/jsonconfig/channel/subdir.mk | 3 + supla-server/Test/src/datalogger/subdir.mk | 8 +- .../Test/src/device/extended_value/subdir.mk | 5 +- supla-server/Test/src/device/value/subdir.mk | 5 +- supla-server/Test/src/ipc/subdir.mk | 6 + supla-server/src/cyclictasks/agent.cpp | 2 + .../general_purpose_text_logger.cpp | 75 +++++ .../datalogger/general_purpose_text_logger.h | 38 +++ .../general_purpose_text_logger_dao.cpp | 66 +++++ .../general_purpose_text_logger_dao.h | 37 +++ supla-server/src/device/devicechannel.cpp | 14 +- .../channel_extended_value_factory.cpp | 7 + ...el_general_purpose_text_extended_value.cpp | 86 ++++++ ...nnel_general_purpose_text_extended_value.h | 42 +++ .../channel_general_purpose_text_value.cpp | 61 ++++ .../channel_general_purpose_text_value.h | 37 +++ .../device/value/channel_value_factory.cpp | 5 + .../ipc/abstract_get_gpt_value_command.cpp | 48 +++ .../src/ipc/abstract_get_gpt_value_command.h | 38 +++ .../src/ipc/get_gpt_value_command.cpp | 55 ++++ supla-server/src/ipc/get_gpt_value_command.h | 36 +++ supla-server/src/ipc/ipc_ctrl.cpp | 2 + .../channel/general_purpose_text_config.cpp | 106 +++++++ .../channel/general_purpose_text_config.h | 41 +++ .../mqtt_abstract_state_message_provider.cpp | 30 +- .../mqtt_abstract_state_message_provider.h | 4 + supla-server/src/push/push_notification.cpp | 3 +- .../push/PushNotificationIntegrationTest.cpp | 17 ++ ...elayedValueBasedTriggerIntegrationTest.cpp | 125 ++++++++ .../vbt/ValueBasedTriggerIntegrationTest.cpp | 54 ++++ .../src/test/push/PushNotificationTest.cpp | 16 + .../src/test/vbt/OnChangeConditionTest.cpp | 275 ++++++++++++++++++ .../src/tsdb/tsdb_access_provider.cpp | 2 +- .../src/vbt/vbt_on_change_condition.cpp | 145 ++++++++- .../src/vbt/vbt_on_change_condition.h | 13 +- supla-server/src/vbt/vbt_value.cpp | 5 + supla-server/src/vbt/vbt_value.h | 2 + 56 files changed, 1852 insertions(+), 21 deletions(-) create mode 100644 sql/AddDelayedGeneralPurposeTextValueBasedTrigger.sql create mode 100644 sql/AddGeneralPurposeText.sql create mode 100644 sql/AddGeneralPurposeTextValueBasedTrigger.sql create mode 100644 sql/TSDB_AddGeneralPurposeText.sql create mode 100644 supla-client/Android/jni/channel_config_general_purpose_text.cpp create mode 100644 supla-client/Android/jni/channel_config_general_purpose_text.h create mode 100644 supla-server/src/datalogger/general_purpose_text_logger.cpp create mode 100644 supla-server/src/datalogger/general_purpose_text_logger.h create mode 100644 supla-server/src/datalogger/general_purpose_text_logger_dao.cpp create mode 100644 supla-server/src/datalogger/general_purpose_text_logger_dao.h create mode 100644 supla-server/src/device/extended_value/channel_general_purpose_text_extended_value.cpp create mode 100644 supla-server/src/device/extended_value/channel_general_purpose_text_extended_value.h create mode 100644 supla-server/src/device/value/channel_general_purpose_text_value.cpp create mode 100644 supla-server/src/device/value/channel_general_purpose_text_value.h create mode 100644 supla-server/src/ipc/abstract_get_gpt_value_command.cpp create mode 100644 supla-server/src/ipc/abstract_get_gpt_value_command.h create mode 100644 supla-server/src/ipc/get_gpt_value_command.cpp create mode 100644 supla-server/src/ipc/get_gpt_value_command.h create mode 100644 supla-server/src/jsonconfig/channel/general_purpose_text_config.cpp create mode 100644 supla-server/src/jsonconfig/channel/general_purpose_text_config.h diff --git a/sql/AddDelayedGeneralPurposeTextValueBasedTrigger.sql b/sql/AddDelayedGeneralPurposeTextValueBasedTrigger.sql new file mode 100644 index 0000000000..36ca0161e6 --- /dev/null +++ b/sql/AddDelayedGeneralPurposeTextValueBasedTrigger.sql @@ -0,0 +1,58 @@ +INSERT INTO supla_dev_channel ( + id, + iodevice_id, + user_id, + channel_number, + caption, + type, + func, + flist, + param1, + param2, + param3, + text_param1, + text_param2, + text_param3, + alt_icon, + hidden, + location_id, + flags, + user_icon_id, + user_config, + param4, + properties, + sub_device_id, + conflict_details +) +VALUES ( + 320, + 83, + 2, + 100, + 'GPT delayed', + 9020, + 540, + 0, + 0, + 0, + 0, + NULL, + NULL, + NULL, + NULL, + 0, + NULL, + 0, + NULL, + '[]', + 0, + NULL, + 0, + NULL +); + +INSERT INTO supla_push_notification(id, user_id, channel_id, iodevice_id, managed_by_device, title, body, sound) +VALUES(500, 2, 320, 83, 0, 'Abcd', 'Efgh', 11); + +INSERT INTO supla_value_based_trigger (id, user_id, owning_channel_id, channel_id, channel_group_id, scene_id, schedule_id, push_notification_id, `trigger`, action, action_param, enabled) +VALUES(34, 2, 320, NULL, NULL, NULL, NULL, 500, '{"on_change_to":{"eq":"match","duration_sec":1}}', 220, NULL, 1); \ No newline at end of file diff --git a/sql/AddGeneralPurposeText.sql b/sql/AddGeneralPurposeText.sql new file mode 100644 index 0000000000..deddc94ad3 --- /dev/null +++ b/sql/AddGeneralPurposeText.sql @@ -0,0 +1,23 @@ +-- Supla general_purpose_text channel type - MariaDB migration +-- v. >= 29 + +CREATE TABLE IF NOT EXISTS `supla_gp_text_log` ( + `channel_id` int(11) NOT NULL, + `date` datetime NOT NULL COMMENT '(DC2Type:stringdatetime)', + `value` varchar(255) NOT NULL, + PRIMARY KEY (`channel_id`,`date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +DROP PROCEDURE IF EXISTS `supla_add_gp_text_log_item`; + +DELIMITER ;; +CREATE DEFINER=`supla`@`localhost` PROCEDURE `supla_add_gp_text_log_item`( + IN `_channel_id` INT, + IN `_value` VARCHAR(255) +) + NOT DETERMINISTIC + CONTAINS SQL + SQL SECURITY DEFINER +INSERT INTO `supla_gp_text_log`(`channel_id`, `date`, `value`) +VALUES (_channel_id, UTC_TIMESTAMP(), _value) ;; +DELIMITER ; diff --git a/sql/AddGeneralPurposeTextValueBasedTrigger.sql b/sql/AddGeneralPurposeTextValueBasedTrigger.sql new file mode 100644 index 0000000000..02a9207c41 --- /dev/null +++ b/sql/AddGeneralPurposeTextValueBasedTrigger.sql @@ -0,0 +1,81 @@ +INSERT INTO supla_dev_channel ( + id, + iodevice_id, + user_id, + channel_number, + caption, + type, + func, + flist, + param1, + param2, + param3, + text_param1, + text_param2, + text_param3, + alt_icon, + hidden, + location_id, + flags, + user_icon_id, + user_config, + param4, + properties, + sub_device_id, + conflict_details +) +VALUES ( + 320, + 83, + 2, + 99, + 'GPT Trigger Channel', + 9020, + 540, + 0, + 0, + 0, + 0, + NULL, + NULL, + NULL, + NULL, + 0, + NULL, + 0, + NULL, + '[]', + 0, + NULL, + 0, + NULL +); + +INSERT INTO supla_value_based_trigger ( + id, + user_id, + owning_channel_id, + channel_id, + channel_group_id, + scene_id, + schedule_id, + push_notification_id, + `trigger`, + action, + action_param, + enabled +) +VALUES ( + 34, + 2, + 320, + NULL, + NULL, + NULL, + NULL, + 500, + '{"on_change_to":{"eq":"match"}}', + 220, + NULL, + 1 +); diff --git a/sql/TSDB_AddGeneralPurposeText.sql b/sql/TSDB_AddGeneralPurposeText.sql new file mode 100644 index 0000000000..01a6d7c5e4 --- /dev/null +++ b/sql/TSDB_AddGeneralPurposeText.sql @@ -0,0 +1,11 @@ +-- Supla general_purpose_text channel type - TimescaleDB (TSDB) migration +-- v. >= 29 + +CREATE TABLE IF NOT EXISTS supla_gp_text_log ( + channel_id INT NOT NULL, + date TIMESTAMPTZ(0) NOT NULL, + value VARCHAR(255) NOT NULL, + PRIMARY KEY(channel_id, date) +); +COMMENT ON COLUMN supla_gp_text_log.date IS '(DC2Type:stringdatetime)'; +SELECT create_hypertable('supla_gp_text_log', 'date', if_not_exists => TRUE); diff --git a/sql/TSDB_TestDatabaseStructureAndData.sql b/sql/TSDB_TestDatabaseStructureAndData.sql index 7ab65b3ecc..c0e4301616 100644 --- a/sql/TSDB_TestDatabaseStructureAndData.sql +++ b/sql/TSDB_TestDatabaseStructureAndData.sql @@ -36,6 +36,9 @@ SELECT create_hypertable('supla_temphumidity_log', 'date'); CREATE TABLE supla_thermostat_log (channel_id INT NOT NULL, date TIMESTAMPTZ(0) NOT NULL, "on" BOOLEAN NOT NULL, measured_temperature NUMERIC(5, 2) NOT NULL, preset_temperature NUMERIC(5, 2) NOT NULL, PRIMARY KEY(channel_id, date)); COMMENT ON COLUMN supla_thermostat_log.date IS '(DC2Type:stringdatetime)'; SELECT create_hypertable('supla_thermostat_log', 'date'); +CREATE TABLE supla_gp_text_log (channel_id INT NOT NULL, date TIMESTAMPTZ(0) NOT NULL, value VARCHAR(255) NOT NULL, PRIMARY KEY(channel_id, date)); +COMMENT ON COLUMN supla_gp_text_log.date IS '(DC2Type:stringdatetime)'; +SELECT create_hypertable('supla_gp_text_log', 'date'); CREATE OR REPLACE FUNCTION supla_add_em_log_item( _channel_id INT, diff --git a/sql/TestDatabaseStructureAndData.sql b/sql/TestDatabaseStructureAndData.sql index 3d73c06ce1..34128aff25 100644 --- a/sql/TestDatabaseStructureAndData.sql +++ b/sql/TestDatabaseStructureAndData.sql @@ -1054,6 +1054,30 @@ LOCK TABLES `supla_gp_meter_log` WRITE; /*!40000 ALTER TABLE `supla_gp_meter_log` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `supla_gp_text_log` +-- + +DROP TABLE IF EXISTS `supla_gp_text_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `supla_gp_text_log` ( + `channel_id` int(11) NOT NULL, + `date` datetime NOT NULL COMMENT '(DC2Type:stringdatetime)', + `value` varchar(255) NOT NULL, + PRIMARY KEY (`channel_id`,`date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `supla_gp_text_log` +-- + +LOCK TABLES `supla_gp_text_log` WRITE; +/*!40000 ALTER TABLE `supla_gp_text_log` DISABLE KEYS */; +/*!40000 ALTER TABLE `supla_gp_text_log` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `supla_ic_log` -- @@ -2564,6 +2588,23 @@ DELIMITER ; /*!50003 SET collation_connection = @saved_col_connection */ ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ; +/*!50003 DROP PROCEDURE IF EXISTS `supla_add_gp_text_log_item` */; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = utf8mb4 */ ; +/*!50003 SET character_set_results = utf8mb4 */ ; +/*!50003 SET collation_connection = utf8mb4_general_ci */ ; +DELIMITER ;; +CREATE DEFINER=`supla`@`localhost` PROCEDURE `supla_add_gp_text_log_item`(IN `_channel_id` INT, IN `_value` VARCHAR(255)) +INSERT INTO `supla_gp_text_log`(`channel_id`, `date`, `value`) VALUES (_channel_id, UTC_TIMESTAMP(), _value) ;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ; /*!50003 DROP PROCEDURE IF EXISTS `supla_add_ic_log_item` */; ALTER DATABASE `supla_test` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci ; /*!50003 SET @saved_cs_client = @@character_set_client */ ; diff --git a/supla-client/Android/jni/Android.mk b/supla-client/Android/jni/Android.mk index dee82577e2..3f40eba8bb 100755 --- a/supla-client/Android/jni/Android.mk +++ b/supla-client/Android/jni/Android.mk @@ -29,6 +29,7 @@ LOCAL_SRC_FILES := supla.cpp \ channel_config_weekly_schedule.cpp \ channel_config_general_purpose_measurement.cpp \ channel_config_general_purpose_meter.cpp \ + channel_config_general_purpose_text.cpp \ channel_config_roller_shutter.cpp \ channel_config_facade_blind.cpp \ channel_config_container.cpp \ diff --git a/supla-client/Android/jni/channel_config.cpp b/supla-client/Android/jni/channel_config.cpp index 352e0fe713..ed28597fbd 100644 --- a/supla-client/Android/jni/channel_config.cpp +++ b/supla-client/Android/jni/channel_config.cpp @@ -24,6 +24,7 @@ #include "channel_config_facade_blind.h" #include "channel_config_general_purpose_measurement.h" #include "channel_config_general_purpose_meter.h" +#include "channel_config_general_purpose_text.h" #include "channel_config_hvac.h" #include "channel_config_roller_shutter.h" #include "channel_config_weekly_schedule.h" @@ -111,6 +112,14 @@ jobject supla_channel_config_to_jobject(JNIEnv *env, TSCS_ChannelConfig *config, (TChannelConfig_GeneralPurposeMeter *)config->Config); } break; + case SUPLA_CHANNELFNC_GENERAL_PURPOSE_TEXT: + if (config->ConfigType == SUPLA_CONFIG_TYPE_DEFAULT && + sizeof(TChannelConfig_GeneralPurposeText) == config->ConfigSize) { + return supla_cc_gp_text_to_jobject( + env, config->ChannelId, config->Func, crc32, + (TChannelConfig_GeneralPurposeText *)config->Config); + } + break; case SUPLA_CHANNELFNC_CONTROLLINGTHEROLLERSHUTTER: case SUPLA_CHANNELFNC_CONTROLLINGTHEROOFWINDOW: if (config->ConfigType == SUPLA_CONFIG_TYPE_DEFAULT && diff --git a/supla-client/Android/jni/channel_config_general_purpose_text.cpp b/supla-client/Android/jni/channel_config_general_purpose_text.cpp new file mode 100644 index 0000000000..047a6fe49e --- /dev/null +++ b/supla-client/Android/jni/channel_config_general_purpose_text.cpp @@ -0,0 +1,42 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "channel_config_general_purpose_text.h" + +#include "supla.h" + +jobject supla_cc_gp_text_to_jobject(JNIEnv *env, _supla_int_t channel_id, + _supla_int_t func, jlong crc32, + TChannelConfig_GeneralPurposeText *config) { + jclass config_cls = env->FindClass( + "org/supla/android/data/source/remote/gpt/" + "SuplaChannelGeneralPurposeTextConfig"); + + jmethodID method_init = env->GetMethodID( + config_cls, "", + "(ILjava/lang/Integer;JZI)V"); + + jobject result = env->NewObject( + config_cls, method_init, channel_id, supla_NewInt(env, func), crc32, + config->KeepHistory ? JNI_TRUE : JNI_FALSE, + (jint)config->RefreshIntervalMs); + + env->DeleteLocalRef(config_cls); + + return result; +} diff --git a/supla-client/Android/jni/channel_config_general_purpose_text.h b/supla-client/Android/jni/channel_config_general_purpose_text.h new file mode 100644 index 0000000000..8dd73c5bf0 --- /dev/null +++ b/supla-client/Android/jni/channel_config_general_purpose_text.h @@ -0,0 +1,37 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef CHANNEL_CONFIG_GENERAL_PURPOSE_TEXT_H_ +#define CHANNEL_CONFIG_GENERAL_PURPOSE_TEXT_H_ + +#include + +#include "proto.h" + +#ifdef __cplusplus +extern "C" { +#endif + +jobject supla_cc_gp_text_to_jobject(JNIEnv *env, _supla_int_t channel_id, + _supla_int_t func, jlong crc32, + TChannelConfig_GeneralPurposeText *config); + +#ifdef __cplusplus +} +#endif +#endif /*CHANNEL_CONFIG_GENERAL_PURPOSE_TEXT_H_*/ diff --git a/supla-common/proto.h b/supla-common/proto.h index 169a5483cf..724a0c07db 100644 --- a/supla-common/proto.h +++ b/supla-common/proto.h @@ -119,7 +119,7 @@ extern char sproto_tag[SUPLA_TAG_SIZE]; // CS - client -> server // SC - server -> client -#define SUPLA_PROTO_VERSION 28 +#define SUPLA_PROTO_VERSION 29 #define SUPLA_PROTO_VERSION_MIN 1 #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO) || defined(SUPLA_DEVICE) @@ -428,6 +428,7 @@ extern char sproto_tag[SUPLA_TAG_SIZE]; #define SUPLA_CHANNELTYPE_BRIDGE 8000 // ver. >= 12 #define SUPLA_CHANNELTYPE_GENERAL_PURPOSE_MEASUREMENT 9000 // ver. >= 23 #define SUPLA_CHANNELTYPE_GENERAL_PURPOSE_METER 9010 // ver. >= 23 +#define SUPLA_CHANNELTYPE_GENERAL_PURPOSE_TEXT 9020 // ver. >= 29 #define SUPLA_CHANNELTYPE_ENGINE 10000 // ver. >= 12 #define SUPLA_CHANNELTYPE_ACTIONTRIGGER 11000 // ver. >= 16 #define SUPLA_CHANNELTYPE_DIGIGLASS 12000 // ver. >= 12 @@ -491,6 +492,7 @@ extern char sproto_tag[SUPLA_TAG_SIZE]; #define SUPLA_CHANNELFNC_VALVE_PERCENTAGE 510 // ver. >= 12 #define SUPLA_CHANNELFNC_GENERAL_PURPOSE_MEASUREMENT 520 // ver. >= 23 #define SUPLA_CHANNELFNC_GENERAL_PURPOSE_METER 530 // ver. >= 23 +#define SUPLA_CHANNELFNC_GENERAL_PURPOSE_TEXT 540 // ver. >= 29 #define SUPLA_CHANNELFNC_CONTROLLINGTHEENGINESPEED 600 // ver. >= 12 #define SUPLA_CHANNELFNC_ACTIONTRIGGER 700 // ver. >= 16 #define SUPLA_CHANNELFNC_DIGIGLASS_HORIZONTAL 800 // ver. >= 14 @@ -776,6 +778,7 @@ typedef struct { #define EV_TYPE_TIMER_STATE_V1_SEC 51 #define EV_TYPE_CHANNEL_AND_TIMER_STATE_V1 60 #define EV_TYPE_MULTI_VALUE 100 +#define EV_TYPE_GENERAL_PURPOSE_TEXT 110 // ver. >= 29 #define CALCFG_TYPE_THERMOSTAT_DETAILS_V1 10 @@ -3542,6 +3545,7 @@ typedef struct { #define SUPLA_GENERAL_PURPOSE_MEASUREMENT_CHART_TYPE_CANDLE 2 #define SUPLA_GENERAL_PURPOSE_UNIT_SIZE 15 +#define SUPLA_GENERAL_PURPOSE_TEXT_MAX_SIZE 255 // ver. >= 29 // General Purpose Mesurement channel config: // Calculated value is: (value / ValueDivider) * ValueMultiplier + ValueAdded @@ -3647,6 +3651,15 @@ typedef struct { unsigned char Reserved[8]; } TChannelConfig_GeneralPurposeMeter; // v. >= 23 +// General Purpose Text channel config: +typedef struct { + // Refresh interval in milliseconds. 1 - 65535, 0 = default + unsigned _supla_int16_t RefreshIntervalMs; + // Keep history: 0 = no (default), 1 = yes + unsigned char KeepHistory; + unsigned char Reserved[13]; +} TChannelConfig_GeneralPurposeText; // v. >= 29 + #define EM_CT_TYPE_100A_33mA (1ULL << 0) #define EM_CT_TYPE_200A_66mA (1ULL << 1) #define EM_CT_TYPE_400A_133mA (1ULL << 2) diff --git a/supla-server/Debug/src/datalogger/subdir.mk b/supla-server/Debug/src/datalogger/subdir.mk index 997555124b..e9dc4bf350 100644 --- a/supla-server/Debug/src/datalogger/subdir.mk +++ b/supla-server/Debug/src/datalogger/subdir.mk @@ -12,6 +12,8 @@ CPP_SRCS += \ ../src/datalogger/general_purpose_measurement_logger_dao.cpp \ ../src/datalogger/general_purpose_meter_logger.cpp \ ../src/datalogger/general_purpose_meter_logger_dao.cpp \ +../src/datalogger/general_purpose_text_logger.cpp \ +../src/datalogger/general_purpose_text_logger_dao.cpp \ ../src/datalogger/hp_thermostat_logger.cpp \ ../src/datalogger/hp_thermostat_logger_dao.cpp \ ../src/datalogger/impulse_logger.cpp \ @@ -36,6 +38,8 @@ CPP_DEPS += \ ./src/datalogger/general_purpose_measurement_logger_dao.d \ ./src/datalogger/general_purpose_meter_logger.d \ ./src/datalogger/general_purpose_meter_logger_dao.d \ +./src/datalogger/general_purpose_text_logger.d \ +./src/datalogger/general_purpose_text_logger_dao.d \ ./src/datalogger/hp_thermostat_logger.d \ ./src/datalogger/hp_thermostat_logger_dao.d \ ./src/datalogger/impulse_logger.d \ @@ -60,6 +64,8 @@ OBJS += \ ./src/datalogger/general_purpose_measurement_logger_dao.o \ ./src/datalogger/general_purpose_meter_logger.o \ ./src/datalogger/general_purpose_meter_logger_dao.o \ +./src/datalogger/general_purpose_text_logger.o \ +./src/datalogger/general_purpose_text_logger_dao.o \ ./src/datalogger/hp_thermostat_logger.o \ ./src/datalogger/hp_thermostat_logger_dao.o \ ./src/datalogger/impulse_logger.o \ diff --git a/supla-server/Debug/src/device/extended_value/subdir.mk b/supla-server/Debug/src/device/extended_value/subdir.mk index e4fd17c350..f916ff17e7 100644 --- a/supla-server/Debug/src/device/extended_value/subdir.mk +++ b/supla-server/Debug/src/device/extended_value/subdir.mk @@ -13,6 +13,7 @@ CPP_SRCS += \ ../src/device/extended_value/channel_extended_value_factory.cpp \ ../src/device/extended_value/channel_hp_thermostat_ev_decorator.cpp \ ../src/device/extended_value/channel_ic_extended_value.cpp \ +../src/device/extended_value/channel_general_purpose_text_extended_value.cpp \ ../src/device/extended_value/channel_multi_extended_value.cpp \ ../src/device/extended_value/channel_state_extended_value.cpp \ ../src/device/extended_value/channel_thermostat_extended_value.cpp \ @@ -28,6 +29,7 @@ CPP_DEPS += \ ./src/device/extended_value/channel_extended_value_factory.d \ ./src/device/extended_value/channel_hp_thermostat_ev_decorator.d \ ./src/device/extended_value/channel_ic_extended_value.d \ +./src/device/extended_value/channel_general_purpose_text_extended_value.d \ ./src/device/extended_value/channel_multi_extended_value.d \ ./src/device/extended_value/channel_state_extended_value.d \ ./src/device/extended_value/channel_thermostat_extended_value.d \ @@ -43,6 +45,7 @@ OBJS += \ ./src/device/extended_value/channel_extended_value_factory.o \ ./src/device/extended_value/channel_hp_thermostat_ev_decorator.o \ ./src/device/extended_value/channel_ic_extended_value.o \ +./src/device/extended_value/channel_general_purpose_text_extended_value.o \ ./src/device/extended_value/channel_multi_extended_value.o \ ./src/device/extended_value/channel_state_extended_value.o \ ./src/device/extended_value/channel_thermostat_extended_value.o \ diff --git a/supla-server/Debug/src/device/value/subdir.mk b/supla-server/Debug/src/device/value/subdir.mk index bfe99ea3ef..456848cd6c 100644 --- a/supla-server/Debug/src/device/value/subdir.mk +++ b/supla-server/Debug/src/device/value/subdir.mk @@ -16,6 +16,7 @@ CPP_SRCS += \ ../src/device/value/channel_general_purpose_base_value.cpp \ ../src/device/value/channel_general_purpose_measurement_value.cpp \ ../src/device/value/channel_general_purpose_meter_value.cpp \ +../src/device/value/channel_general_purpose_text_value.cpp \ ../src/device/value/channel_hp_thermostat_value.cpp \ ../src/device/value/channel_hvac_value.cpp \ ../src/device/value/channel_hvac_value_with_temphum.cpp \ @@ -43,6 +44,7 @@ CPP_DEPS += \ ./src/device/value/channel_general_purpose_base_value.d \ ./src/device/value/channel_general_purpose_measurement_value.d \ ./src/device/value/channel_general_purpose_meter_value.d \ +./src/device/value/channel_general_purpose_text_value.d \ ./src/device/value/channel_hp_thermostat_value.d \ ./src/device/value/channel_hvac_value.d \ ./src/device/value/channel_hvac_value_with_temphum.d \ @@ -70,6 +72,7 @@ OBJS += \ ./src/device/value/channel_general_purpose_base_value.o \ ./src/device/value/channel_general_purpose_measurement_value.o \ ./src/device/value/channel_general_purpose_meter_value.o \ +./src/device/value/channel_general_purpose_text_value.o \ ./src/device/value/channel_hp_thermostat_value.o \ ./src/device/value/channel_hvac_value.o \ ./src/device/value/channel_hvac_value_with_temphum.o \ @@ -97,7 +100,7 @@ src/device/value/%.o: ../src/device/value/%.cpp src/device/value/subdir.mk clean: clean-src-2f-device-2f-value clean-src-2f-device-2f-value: - -$(RM) ./src/device/value/abstract_channel_value.d ./src/device/value/abstract_channel_value.o ./src/device/value/abstract_rs_value.d ./src/device/value/abstract_rs_value.o ./src/device/value/channel_binary_sensor_value.d ./src/device/value/channel_binary_sensor_value.o ./src/device/value/channel_container_value.d ./src/device/value/channel_container_value.o ./src/device/value/channel_dgf_value.d ./src/device/value/channel_dgf_value.o ./src/device/value/channel_em_value.d ./src/device/value/channel_em_value.o ./src/device/value/channel_fb_value.d ./src/device/value/channel_fb_value.o ./src/device/value/channel_floating_point_sensor_value.d ./src/device/value/channel_floating_point_sensor_value.o ./src/device/value/channel_gate_value.d ./src/device/value/channel_gate_value.o ./src/device/value/channel_general_purpose_base_value.d ./src/device/value/channel_general_purpose_base_value.o ./src/device/value/channel_general_purpose_measurement_value.d ./src/device/value/channel_general_purpose_measurement_value.o ./src/device/value/channel_general_purpose_meter_value.d ./src/device/value/channel_general_purpose_meter_value.o ./src/device/value/channel_hp_thermostat_value.d ./src/device/value/channel_hp_thermostat_value.o ./src/device/value/channel_hvac_value.d ./src/device/value/channel_hvac_value.o ./src/device/value/channel_hvac_value_with_temphum.d ./src/device/value/channel_hvac_value_with_temphum.o ./src/device/value/channel_ic_value.d ./src/device/value/channel_ic_value.o ./src/device/value/channel_onoff_value.d ./src/device/value/channel_onoff_value.o ./src/device/value/channel_openclosed_value.d ./src/device/value/channel_openclosed_value.o ./src/device/value/channel_rgbw_value.d ./src/device/value/channel_rgbw_value.o ./src/device/value/channel_rs_value.d ./src/device/value/channel_rs_value.o ./src/device/value/channel_temphum_value.d ./src/device/value/channel_temphum_value.o ./src/device/value/channel_value.d ./src/device/value/channel_value.o ./src/device/value/channel_value_envelope.d ./src/device/value/channel_value_envelope.o ./src/device/value/channel_value_factory.d ./src/device/value/channel_value_factory.o ./src/device/value/channel_valve_value.d ./src/device/value/channel_valve_value.o + -$(RM) ./src/device/value/abstract_channel_value.d ./src/device/value/abstract_channel_value.o ./src/device/value/abstract_rs_value.d ./src/device/value/abstract_rs_value.o ./src/device/value/channel_binary_sensor_value.d ./src/device/value/channel_binary_sensor_value.o ./src/device/value/channel_container_value.d ./src/device/value/channel_container_value.o ./src/device/value/channel_dgf_value.d ./src/device/value/channel_dgf_value.o ./src/device/value/channel_em_value.d ./src/device/value/channel_em_value.o ./src/device/value/channel_fb_value.d ./src/device/value/channel_fb_value.o ./src/device/value/channel_floating_point_sensor_value.d ./src/device/value/channel_floating_point_sensor_value.o ./src/device/value/channel_gate_value.d ./src/device/value/channel_gate_value.o ./src/device/value/channel_general_purpose_base_value.d ./src/device/value/channel_general_purpose_base_value.o ./src/device/value/channel_general_purpose_measurement_value.d ./src/device/value/channel_general_purpose_measurement_value.o ./src/device/value/channel_general_purpose_meter_value.d ./src/device/value/channel_general_purpose_meter_value.o ./src/device/value/channel_general_purpose_text_value.d ./src/device/value/channel_general_purpose_text_value.o ./src/device/value/channel_hp_thermostat_value.d ./src/device/value/channel_hp_thermostat_value.o ./src/device/value/channel_hvac_value.d ./src/device/value/channel_hvac_value.o ./src/device/value/channel_hvac_value_with_temphum.d ./src/device/value/channel_hvac_value_with_temphum.o ./src/device/value/channel_ic_value.d ./src/device/value/channel_ic_value.o ./src/device/value/channel_onoff_value.d ./src/device/value/channel_onoff_value.o ./src/device/value/channel_openclosed_value.d ./src/device/value/channel_openclosed_value.o ./src/device/value/channel_rgbw_value.d ./src/device/value/channel_rgbw_value.o ./src/device/value/channel_rs_value.d ./src/device/value/channel_rs_value.o ./src/device/value/channel_temphum_value.d ./src/device/value/channel_temphum_value.o ./src/device/value/channel_value.d ./src/device/value/channel_value.o ./src/device/value/channel_value_envelope.d ./src/device/value/channel_value_envelope.o ./src/device/value/channel_value_factory.d ./src/device/value/channel_value_factory.o ./src/device/value/channel_valve_value.d ./src/device/value/channel_valve_value.o .PHONY: clean-src-2f-device-2f-value diff --git a/supla-server/Debug/src/ipc/subdir.mk b/supla-server/Debug/src/ipc/subdir.mk index 381698784f..5010af54bf 100644 --- a/supla-server/Debug/src/ipc/subdir.mk +++ b/supla-server/Debug/src/ipc/subdir.mk @@ -21,6 +21,7 @@ CPP_SRCS += \ ../src/ipc/abstract_get_em_value_command.cpp \ ../src/ipc/abstract_get_facade_blind_value_command.cpp \ ../src/ipc/abstract_get_gpm_value_command.cpp \ +../src/ipc/abstract_get_gpt_value_command.cpp \ ../src/ipc/abstract_get_humidity_command.cpp \ ../src/ipc/abstract_get_hvac_value_command.cpp \ ../src/ipc/abstract_get_ic_value_command.cpp \ @@ -88,6 +89,7 @@ CPP_SRCS += \ ../src/ipc/get_em_value_command.cpp \ ../src/ipc/get_facade_blind_value_command.cpp \ ../src/ipc/get_gpm_value_command.cpp \ +../src/ipc/get_gpt_value_command.cpp \ ../src/ipc/get_humidity_command.cpp \ ../src/ipc/get_hvac_value_command.cpp \ ../src/ipc/get_ic_value_command.cpp \ @@ -160,6 +162,7 @@ CPP_DEPS += \ ./src/ipc/abstract_get_em_value_command.d \ ./src/ipc/abstract_get_facade_blind_value_command.d \ ./src/ipc/abstract_get_gpm_value_command.d \ +./src/ipc/abstract_get_gpt_value_command.d \ ./src/ipc/abstract_get_humidity_command.d \ ./src/ipc/abstract_get_hvac_value_command.d \ ./src/ipc/abstract_get_ic_value_command.d \ @@ -227,6 +230,7 @@ CPP_DEPS += \ ./src/ipc/get_em_value_command.d \ ./src/ipc/get_facade_blind_value_command.d \ ./src/ipc/get_gpm_value_command.d \ +./src/ipc/get_gpt_value_command.d \ ./src/ipc/get_humidity_command.d \ ./src/ipc/get_hvac_value_command.d \ ./src/ipc/get_ic_value_command.d \ @@ -299,6 +303,7 @@ OBJS += \ ./src/ipc/abstract_get_em_value_command.o \ ./src/ipc/abstract_get_facade_blind_value_command.o \ ./src/ipc/abstract_get_gpm_value_command.o \ +./src/ipc/abstract_get_gpt_value_command.o \ ./src/ipc/abstract_get_humidity_command.o \ ./src/ipc/abstract_get_hvac_value_command.o \ ./src/ipc/abstract_get_ic_value_command.o \ @@ -366,6 +371,7 @@ OBJS += \ ./src/ipc/get_em_value_command.o \ ./src/ipc/get_facade_blind_value_command.o \ ./src/ipc/get_gpm_value_command.o \ +./src/ipc/get_gpt_value_command.o \ ./src/ipc/get_humidity_command.o \ ./src/ipc/get_hvac_value_command.o \ ./src/ipc/get_ic_value_command.o \ diff --git a/supla-server/Release/src/datalogger/subdir.mk b/supla-server/Release/src/datalogger/subdir.mk index 30c3963191..b13aacae44 100644 --- a/supla-server/Release/src/datalogger/subdir.mk +++ b/supla-server/Release/src/datalogger/subdir.mk @@ -12,6 +12,8 @@ CPP_SRCS += \ ../src/datalogger/general_purpose_measurement_logger_dao.cpp \ ../src/datalogger/general_purpose_meter_logger.cpp \ ../src/datalogger/general_purpose_meter_logger_dao.cpp \ +../src/datalogger/general_purpose_text_logger.cpp \ +../src/datalogger/general_purpose_text_logger_dao.cpp \ ../src/datalogger/hp_thermostat_logger.cpp \ ../src/datalogger/hp_thermostat_logger_dao.cpp \ ../src/datalogger/impulse_logger.cpp \ @@ -36,6 +38,8 @@ CPP_DEPS += \ ./src/datalogger/general_purpose_measurement_logger_dao.d \ ./src/datalogger/general_purpose_meter_logger.d \ ./src/datalogger/general_purpose_meter_logger_dao.d \ +./src/datalogger/general_purpose_text_logger.d \ +./src/datalogger/general_purpose_text_logger_dao.d \ ./src/datalogger/hp_thermostat_logger.d \ ./src/datalogger/hp_thermostat_logger_dao.d \ ./src/datalogger/impulse_logger.d \ @@ -60,6 +64,8 @@ OBJS += \ ./src/datalogger/general_purpose_measurement_logger_dao.o \ ./src/datalogger/general_purpose_meter_logger.o \ ./src/datalogger/general_purpose_meter_logger_dao.o \ +./src/datalogger/general_purpose_text_logger.o \ +./src/datalogger/general_purpose_text_logger_dao.o \ ./src/datalogger/hp_thermostat_logger.o \ ./src/datalogger/hp_thermostat_logger_dao.o \ ./src/datalogger/impulse_logger.o \ diff --git a/supla-server/Release/src/device/extended_value/subdir.mk b/supla-server/Release/src/device/extended_value/subdir.mk index ef09c02bfe..fb98befbd3 100644 --- a/supla-server/Release/src/device/extended_value/subdir.mk +++ b/supla-server/Release/src/device/extended_value/subdir.mk @@ -13,6 +13,7 @@ CPP_SRCS += \ ../src/device/extended_value/channel_extended_value_factory.cpp \ ../src/device/extended_value/channel_hp_thermostat_ev_decorator.cpp \ ../src/device/extended_value/channel_ic_extended_value.cpp \ +../src/device/extended_value/channel_general_purpose_text_extended_value.cpp \ ../src/device/extended_value/channel_multi_extended_value.cpp \ ../src/device/extended_value/channel_state_extended_value.cpp \ ../src/device/extended_value/channel_thermostat_extended_value.cpp \ @@ -28,6 +29,7 @@ CPP_DEPS += \ ./src/device/extended_value/channel_extended_value_factory.d \ ./src/device/extended_value/channel_hp_thermostat_ev_decorator.d \ ./src/device/extended_value/channel_ic_extended_value.d \ +./src/device/extended_value/channel_general_purpose_text_extended_value.d \ ./src/device/extended_value/channel_multi_extended_value.d \ ./src/device/extended_value/channel_state_extended_value.d \ ./src/device/extended_value/channel_thermostat_extended_value.d \ @@ -43,6 +45,7 @@ OBJS += \ ./src/device/extended_value/channel_extended_value_factory.o \ ./src/device/extended_value/channel_hp_thermostat_ev_decorator.o \ ./src/device/extended_value/channel_ic_extended_value.o \ +./src/device/extended_value/channel_general_purpose_text_extended_value.o \ ./src/device/extended_value/channel_multi_extended_value.o \ ./src/device/extended_value/channel_state_extended_value.o \ ./src/device/extended_value/channel_thermostat_extended_value.o \ diff --git a/supla-server/Release/src/device/value/subdir.mk b/supla-server/Release/src/device/value/subdir.mk index 1de42bab3a..d0f357b60f 100644 --- a/supla-server/Release/src/device/value/subdir.mk +++ b/supla-server/Release/src/device/value/subdir.mk @@ -16,6 +16,7 @@ CPP_SRCS += \ ../src/device/value/channel_general_purpose_base_value.cpp \ ../src/device/value/channel_general_purpose_measurement_value.cpp \ ../src/device/value/channel_general_purpose_meter_value.cpp \ +../src/device/value/channel_general_purpose_text_value.cpp \ ../src/device/value/channel_hp_thermostat_value.cpp \ ../src/device/value/channel_hvac_value.cpp \ ../src/device/value/channel_hvac_value_with_temphum.cpp \ @@ -43,6 +44,7 @@ CPP_DEPS += \ ./src/device/value/channel_general_purpose_base_value.d \ ./src/device/value/channel_general_purpose_measurement_value.d \ ./src/device/value/channel_general_purpose_meter_value.d \ +./src/device/value/channel_general_purpose_text_value.d \ ./src/device/value/channel_hp_thermostat_value.d \ ./src/device/value/channel_hvac_value.d \ ./src/device/value/channel_hvac_value_with_temphum.d \ @@ -70,6 +72,7 @@ OBJS += \ ./src/device/value/channel_general_purpose_base_value.o \ ./src/device/value/channel_general_purpose_measurement_value.o \ ./src/device/value/channel_general_purpose_meter_value.o \ +./src/device/value/channel_general_purpose_text_value.o \ ./src/device/value/channel_hp_thermostat_value.o \ ./src/device/value/channel_hvac_value.o \ ./src/device/value/channel_hvac_value_with_temphum.o \ diff --git a/supla-server/Release/src/ipc/subdir.mk b/supla-server/Release/src/ipc/subdir.mk index b3b203ad44..93259f147c 100644 --- a/supla-server/Release/src/ipc/subdir.mk +++ b/supla-server/Release/src/ipc/subdir.mk @@ -21,6 +21,7 @@ CPP_SRCS += \ ../src/ipc/abstract_get_em_value_command.cpp \ ../src/ipc/abstract_get_facade_blind_value_command.cpp \ ../src/ipc/abstract_get_gpm_value_command.cpp \ +../src/ipc/abstract_get_gpt_value_command.cpp \ ../src/ipc/abstract_get_humidity_command.cpp \ ../src/ipc/abstract_get_hvac_value_command.cpp \ ../src/ipc/abstract_get_ic_value_command.cpp \ @@ -88,6 +89,7 @@ CPP_SRCS += \ ../src/ipc/get_em_value_command.cpp \ ../src/ipc/get_facade_blind_value_command.cpp \ ../src/ipc/get_gpm_value_command.cpp \ +../src/ipc/get_gpt_value_command.cpp \ ../src/ipc/get_humidity_command.cpp \ ../src/ipc/get_hvac_value_command.cpp \ ../src/ipc/get_ic_value_command.cpp \ @@ -160,6 +162,7 @@ CPP_DEPS += \ ./src/ipc/abstract_get_em_value_command.d \ ./src/ipc/abstract_get_facade_blind_value_command.d \ ./src/ipc/abstract_get_gpm_value_command.d \ +./src/ipc/abstract_get_gpt_value_command.d \ ./src/ipc/abstract_get_humidity_command.d \ ./src/ipc/abstract_get_hvac_value_command.d \ ./src/ipc/abstract_get_ic_value_command.d \ @@ -227,6 +230,7 @@ CPP_DEPS += \ ./src/ipc/get_em_value_command.d \ ./src/ipc/get_facade_blind_value_command.d \ ./src/ipc/get_gpm_value_command.d \ +./src/ipc/get_gpt_value_command.d \ ./src/ipc/get_humidity_command.d \ ./src/ipc/get_hvac_value_command.d \ ./src/ipc/get_ic_value_command.d \ @@ -299,6 +303,7 @@ OBJS += \ ./src/ipc/abstract_get_em_value_command.o \ ./src/ipc/abstract_get_facade_blind_value_command.o \ ./src/ipc/abstract_get_gpm_value_command.o \ +./src/ipc/abstract_get_gpt_value_command.o \ ./src/ipc/abstract_get_humidity_command.o \ ./src/ipc/abstract_get_hvac_value_command.o \ ./src/ipc/abstract_get_ic_value_command.o \ @@ -366,6 +371,7 @@ OBJS += \ ./src/ipc/get_em_value_command.o \ ./src/ipc/get_facade_blind_value_command.o \ ./src/ipc/get_gpm_value_command.o \ +./src/ipc/get_gpt_value_command.o \ ./src/ipc/get_humidity_command.o \ ./src/ipc/get_hvac_value_command.o \ ./src/ipc/get_ic_value_command.o \ diff --git a/supla-server/Release/src/jsonconfig/channel/subdir.mk b/supla-server/Release/src/jsonconfig/channel/subdir.mk index c46b33e4d5..b9428ccb8b 100644 --- a/supla-server/Release/src/jsonconfig/channel/subdir.mk +++ b/supla-server/Release/src/jsonconfig/channel/subdir.mk @@ -17,6 +17,7 @@ CPP_SRCS += \ ../src/jsonconfig/channel/general_purpose_base_config.cpp \ ../src/jsonconfig/channel/general_purpose_measurement_config.cpp \ ../src/jsonconfig/channel/general_purpose_meter_config.cpp \ +../src/jsonconfig/channel/general_purpose_text_config.cpp \ ../src/jsonconfig/channel/google_home_config.cpp \ ../src/jsonconfig/channel/hvac_config.cpp \ ../src/jsonconfig/channel/impulse_counter_config.cpp \ @@ -42,6 +43,7 @@ CPP_DEPS += \ ./src/jsonconfig/channel/general_purpose_base_config.d \ ./src/jsonconfig/channel/general_purpose_measurement_config.d \ ./src/jsonconfig/channel/general_purpose_meter_config.d \ +./src/jsonconfig/channel/general_purpose_text_config.d \ ./src/jsonconfig/channel/google_home_config.d \ ./src/jsonconfig/channel/hvac_config.d \ ./src/jsonconfig/channel/impulse_counter_config.d \ @@ -67,6 +69,7 @@ OBJS += \ ./src/jsonconfig/channel/general_purpose_base_config.o \ ./src/jsonconfig/channel/general_purpose_measurement_config.o \ ./src/jsonconfig/channel/general_purpose_meter_config.o \ +./src/jsonconfig/channel/general_purpose_text_config.o \ ./src/jsonconfig/channel/google_home_config.o \ ./src/jsonconfig/channel/hvac_config.o \ ./src/jsonconfig/channel/impulse_counter_config.o \ diff --git a/supla-server/Test/src/datalogger/subdir.mk b/supla-server/Test/src/datalogger/subdir.mk index 75d742c0a3..04bff80090 100644 --- a/supla-server/Test/src/datalogger/subdir.mk +++ b/supla-server/Test/src/datalogger/subdir.mk @@ -12,6 +12,8 @@ CPP_SRCS += \ ../src/datalogger/general_purpose_measurement_logger_dao.cpp \ ../src/datalogger/general_purpose_meter_logger.cpp \ ../src/datalogger/general_purpose_meter_logger_dao.cpp \ +../src/datalogger/general_purpose_text_logger.cpp \ +../src/datalogger/general_purpose_text_logger_dao.cpp \ ../src/datalogger/hp_thermostat_logger.cpp \ ../src/datalogger/hp_thermostat_logger_dao.cpp \ ../src/datalogger/impulse_logger.cpp \ @@ -36,6 +38,8 @@ CPP_DEPS += \ ./src/datalogger/general_purpose_measurement_logger_dao.d \ ./src/datalogger/general_purpose_meter_logger.d \ ./src/datalogger/general_purpose_meter_logger_dao.d \ +./src/datalogger/general_purpose_text_logger.d \ +./src/datalogger/general_purpose_text_logger_dao.d \ ./src/datalogger/hp_thermostat_logger.d \ ./src/datalogger/hp_thermostat_logger_dao.d \ ./src/datalogger/impulse_logger.d \ @@ -60,6 +64,8 @@ OBJS += \ ./src/datalogger/general_purpose_measurement_logger_dao.o \ ./src/datalogger/general_purpose_meter_logger.o \ ./src/datalogger/general_purpose_meter_logger_dao.o \ +./src/datalogger/general_purpose_text_logger.o \ +./src/datalogger/general_purpose_text_logger_dao.o \ ./src/datalogger/hp_thermostat_logger.o \ ./src/datalogger/hp_thermostat_logger_dao.o \ ./src/datalogger/impulse_logger.o \ @@ -88,7 +94,7 @@ src/datalogger/%.o: ../src/datalogger/%.cpp src/datalogger/subdir.mk clean: clean-src-2f-datalogger clean-src-2f-datalogger: - -$(RM) ./src/datalogger/abstract_electricity_logger.d ./src/datalogger/abstract_electricity_logger.o ./src/datalogger/abstract_electricity_logger_dao.d ./src/datalogger/abstract_electricity_logger_dao.o ./src/datalogger/current_logger.d ./src/datalogger/current_logger.o ./src/datalogger/current_logger_dao.d ./src/datalogger/current_logger_dao.o ./src/datalogger/general_purpose_measurement_logger.d ./src/datalogger/general_purpose_measurement_logger.o ./src/datalogger/general_purpose_measurement_logger_dao.d ./src/datalogger/general_purpose_measurement_logger_dao.o ./src/datalogger/general_purpose_meter_logger.d ./src/datalogger/general_purpose_meter_logger.o ./src/datalogger/general_purpose_meter_logger_dao.d ./src/datalogger/general_purpose_meter_logger_dao.o ./src/datalogger/hp_thermostat_logger.d ./src/datalogger/hp_thermostat_logger.o ./src/datalogger/hp_thermostat_logger_dao.d ./src/datalogger/hp_thermostat_logger_dao.o ./src/datalogger/impulse_logger.d ./src/datalogger/impulse_logger.o ./src/datalogger/impulse_logger_dao.d ./src/datalogger/impulse_logger_dao.o ./src/datalogger/power_active_logger.d ./src/datalogger/power_active_logger.o ./src/datalogger/power_active_logger_dao.d ./src/datalogger/power_active_logger_dao.o ./src/datalogger/temperature_logger.d ./src/datalogger/temperature_logger.o ./src/datalogger/temperature_logger_dao.d ./src/datalogger/temperature_logger_dao.o ./src/datalogger/total_energy_logger.d ./src/datalogger/total_energy_logger.o ./src/datalogger/total_energy_logger_dao.d ./src/datalogger/total_energy_logger_dao.o ./src/datalogger/voltage_aberration_logger.d ./src/datalogger/voltage_aberration_logger.o ./src/datalogger/voltage_aberration_logger_dao.d ./src/datalogger/voltage_aberration_logger_dao.o ./src/datalogger/voltage_logger.d ./src/datalogger/voltage_logger.o ./src/datalogger/voltage_logger_dao.d ./src/datalogger/voltage_logger_dao.o + -$(RM) ./src/datalogger/abstract_electricity_logger.d ./src/datalogger/abstract_electricity_logger.o ./src/datalogger/abstract_electricity_logger_dao.d ./src/datalogger/abstract_electricity_logger_dao.o ./src/datalogger/current_logger.d ./src/datalogger/current_logger.o ./src/datalogger/current_logger_dao.d ./src/datalogger/current_logger_dao.o ./src/datalogger/general_purpose_measurement_logger.d ./src/datalogger/general_purpose_measurement_logger.o ./src/datalogger/general_purpose_measurement_logger_dao.d ./src/datalogger/general_purpose_measurement_logger_dao.o ./src/datalogger/general_purpose_meter_logger.d ./src/datalogger/general_purpose_meter_logger.o ./src/datalogger/general_purpose_meter_logger_dao.d ./src/datalogger/general_purpose_meter_logger_dao.o ./src/datalogger/general_purpose_text_logger.d ./src/datalogger/general_purpose_text_logger.o ./src/datalogger/general_purpose_text_logger_dao.d ./src/datalogger/general_purpose_text_logger_dao.o ./src/datalogger/hp_thermostat_logger.d ./src/datalogger/hp_thermostat_logger.o ./src/datalogger/hp_thermostat_logger_dao.d ./src/datalogger/hp_thermostat_logger_dao.o ./src/datalogger/impulse_logger.d ./src/datalogger/impulse_logger.o ./src/datalogger/impulse_logger_dao.d ./src/datalogger/impulse_logger_dao.o ./src/datalogger/power_active_logger.d ./src/datalogger/power_active_logger.o ./src/datalogger/power_active_logger_dao.d ./src/datalogger/power_active_logger_dao.o ./src/datalogger/temperature_logger.d ./src/datalogger/temperature_logger.o ./src/datalogger/temperature_logger_dao.d ./src/datalogger/temperature_logger_dao.o ./src/datalogger/total_energy_logger.d ./src/datalogger/total_energy_logger.o ./src/datalogger/total_energy_logger_dao.d ./src/datalogger/total_energy_logger_dao.o ./src/datalogger/voltage_aberration_logger.d ./src/datalogger/voltage_aberration_logger.o ./src/datalogger/voltage_aberration_logger_dao.d ./src/datalogger/voltage_aberration_logger_dao.o ./src/datalogger/voltage_logger.d ./src/datalogger/voltage_logger.o ./src/datalogger/voltage_logger_dao.d ./src/datalogger/voltage_logger_dao.o .PHONY: clean-src-2f-datalogger diff --git a/supla-server/Test/src/device/extended_value/subdir.mk b/supla-server/Test/src/device/extended_value/subdir.mk index 5bd5101362..19c04260ef 100644 --- a/supla-server/Test/src/device/extended_value/subdir.mk +++ b/supla-server/Test/src/device/extended_value/subdir.mk @@ -11,6 +11,7 @@ CPP_SRCS += \ ../src/device/extended_value/channel_extended_value.cpp \ ../src/device/extended_value/channel_extended_value_envelope.cpp \ ../src/device/extended_value/channel_extended_value_factory.cpp \ +../src/device/extended_value/channel_general_purpose_text_extended_value.cpp \ ../src/device/extended_value/channel_hp_thermostat_ev_decorator.cpp \ ../src/device/extended_value/channel_ic_extended_value.cpp \ ../src/device/extended_value/channel_multi_extended_value.cpp \ @@ -26,6 +27,7 @@ CPP_DEPS += \ ./src/device/extended_value/channel_extended_value.d \ ./src/device/extended_value/channel_extended_value_envelope.d \ ./src/device/extended_value/channel_extended_value_factory.d \ +./src/device/extended_value/channel_general_purpose_text_extended_value.d \ ./src/device/extended_value/channel_hp_thermostat_ev_decorator.d \ ./src/device/extended_value/channel_ic_extended_value.d \ ./src/device/extended_value/channel_multi_extended_value.d \ @@ -41,6 +43,7 @@ OBJS += \ ./src/device/extended_value/channel_extended_value.o \ ./src/device/extended_value/channel_extended_value_envelope.o \ ./src/device/extended_value/channel_extended_value_factory.o \ +./src/device/extended_value/channel_general_purpose_text_extended_value.o \ ./src/device/extended_value/channel_hp_thermostat_ev_decorator.o \ ./src/device/extended_value/channel_ic_extended_value.o \ ./src/device/extended_value/channel_multi_extended_value.o \ @@ -61,7 +64,7 @@ src/device/extended_value/%.o: ../src/device/extended_value/%.cpp src/device/ext clean: clean-src-2f-device-2f-extended_value clean-src-2f-device-2f-extended_value: - -$(RM) ./src/device/extended_value/abstract_channel_extended_value.d ./src/device/extended_value/abstract_channel_extended_value.o ./src/device/extended_value/channel_and_timer_state_extended_value.d ./src/device/extended_value/channel_and_timer_state_extended_value.o ./src/device/extended_value/channel_billing_value.d ./src/device/extended_value/channel_billing_value.o ./src/device/extended_value/channel_em_extended_value.d ./src/device/extended_value/channel_em_extended_value.o ./src/device/extended_value/channel_extended_value.d ./src/device/extended_value/channel_extended_value.o ./src/device/extended_value/channel_extended_value_envelope.d ./src/device/extended_value/channel_extended_value_envelope.o ./src/device/extended_value/channel_extended_value_factory.d ./src/device/extended_value/channel_extended_value_factory.o ./src/device/extended_value/channel_hp_thermostat_ev_decorator.d ./src/device/extended_value/channel_hp_thermostat_ev_decorator.o ./src/device/extended_value/channel_ic_extended_value.d ./src/device/extended_value/channel_ic_extended_value.o ./src/device/extended_value/channel_multi_extended_value.d ./src/device/extended_value/channel_multi_extended_value.o ./src/device/extended_value/channel_state_extended_value.d ./src/device/extended_value/channel_state_extended_value.o ./src/device/extended_value/channel_thermostat_extended_value.d ./src/device/extended_value/channel_thermostat_extended_value.o ./src/device/extended_value/timer_state_extended_value.d ./src/device/extended_value/timer_state_extended_value.o + -$(RM) ./src/device/extended_value/abstract_channel_extended_value.d ./src/device/extended_value/abstract_channel_extended_value.o ./src/device/extended_value/channel_and_timer_state_extended_value.d ./src/device/extended_value/channel_and_timer_state_extended_value.o ./src/device/extended_value/channel_billing_value.d ./src/device/extended_value/channel_billing_value.o ./src/device/extended_value/channel_em_extended_value.d ./src/device/extended_value/channel_em_extended_value.o ./src/device/extended_value/channel_extended_value.d ./src/device/extended_value/channel_extended_value.o ./src/device/extended_value/channel_extended_value_envelope.d ./src/device/extended_value/channel_extended_value_envelope.o ./src/device/extended_value/channel_extended_value_factory.d ./src/device/extended_value/channel_extended_value_factory.o ./src/device/extended_value/channel_general_purpose_text_extended_value.d ./src/device/extended_value/channel_general_purpose_text_extended_value.o ./src/device/extended_value/channel_hp_thermostat_ev_decorator.d ./src/device/extended_value/channel_hp_thermostat_ev_decorator.o ./src/device/extended_value/channel_ic_extended_value.d ./src/device/extended_value/channel_ic_extended_value.o ./src/device/extended_value/channel_multi_extended_value.d ./src/device/extended_value/channel_multi_extended_value.o ./src/device/extended_value/channel_state_extended_value.d ./src/device/extended_value/channel_state_extended_value.o ./src/device/extended_value/channel_thermostat_extended_value.d ./src/device/extended_value/channel_thermostat_extended_value.o ./src/device/extended_value/timer_state_extended_value.d ./src/device/extended_value/timer_state_extended_value.o .PHONY: clean-src-2f-device-2f-extended_value diff --git a/supla-server/Test/src/device/value/subdir.mk b/supla-server/Test/src/device/value/subdir.mk index 669120d81f..95befcfe30 100644 --- a/supla-server/Test/src/device/value/subdir.mk +++ b/supla-server/Test/src/device/value/subdir.mk @@ -16,6 +16,7 @@ CPP_SRCS += \ ../src/device/value/channel_general_purpose_base_value.cpp \ ../src/device/value/channel_general_purpose_measurement_value.cpp \ ../src/device/value/channel_general_purpose_meter_value.cpp \ +../src/device/value/channel_general_purpose_text_value.cpp \ ../src/device/value/channel_hp_thermostat_value.cpp \ ../src/device/value/channel_hvac_value.cpp \ ../src/device/value/channel_hvac_value_with_temphum.cpp \ @@ -43,6 +44,7 @@ CPP_DEPS += \ ./src/device/value/channel_general_purpose_base_value.d \ ./src/device/value/channel_general_purpose_measurement_value.d \ ./src/device/value/channel_general_purpose_meter_value.d \ +./src/device/value/channel_general_purpose_text_value.d \ ./src/device/value/channel_hp_thermostat_value.d \ ./src/device/value/channel_hvac_value.d \ ./src/device/value/channel_hvac_value_with_temphum.d \ @@ -70,6 +72,7 @@ OBJS += \ ./src/device/value/channel_general_purpose_base_value.o \ ./src/device/value/channel_general_purpose_measurement_value.o \ ./src/device/value/channel_general_purpose_meter_value.o \ +./src/device/value/channel_general_purpose_text_value.o \ ./src/device/value/channel_hp_thermostat_value.o \ ./src/device/value/channel_hvac_value.o \ ./src/device/value/channel_hvac_value_with_temphum.o \ @@ -97,7 +100,7 @@ src/device/value/%.o: ../src/device/value/%.cpp src/device/value/subdir.mk clean: clean-src-2f-device-2f-value clean-src-2f-device-2f-value: - -$(RM) ./src/device/value/abstract_channel_value.d ./src/device/value/abstract_channel_value.o ./src/device/value/abstract_rs_value.d ./src/device/value/abstract_rs_value.o ./src/device/value/channel_binary_sensor_value.d ./src/device/value/channel_binary_sensor_value.o ./src/device/value/channel_container_value.d ./src/device/value/channel_container_value.o ./src/device/value/channel_dgf_value.d ./src/device/value/channel_dgf_value.o ./src/device/value/channel_em_value.d ./src/device/value/channel_em_value.o ./src/device/value/channel_fb_value.d ./src/device/value/channel_fb_value.o ./src/device/value/channel_floating_point_sensor_value.d ./src/device/value/channel_floating_point_sensor_value.o ./src/device/value/channel_gate_value.d ./src/device/value/channel_gate_value.o ./src/device/value/channel_general_purpose_base_value.d ./src/device/value/channel_general_purpose_base_value.o ./src/device/value/channel_general_purpose_measurement_value.d ./src/device/value/channel_general_purpose_measurement_value.o ./src/device/value/channel_general_purpose_meter_value.d ./src/device/value/channel_general_purpose_meter_value.o ./src/device/value/channel_hp_thermostat_value.d ./src/device/value/channel_hp_thermostat_value.o ./src/device/value/channel_hvac_value.d ./src/device/value/channel_hvac_value.o ./src/device/value/channel_hvac_value_with_temphum.d ./src/device/value/channel_hvac_value_with_temphum.o ./src/device/value/channel_ic_value.d ./src/device/value/channel_ic_value.o ./src/device/value/channel_onoff_value.d ./src/device/value/channel_onoff_value.o ./src/device/value/channel_openclosed_value.d ./src/device/value/channel_openclosed_value.o ./src/device/value/channel_rgbw_value.d ./src/device/value/channel_rgbw_value.o ./src/device/value/channel_rs_value.d ./src/device/value/channel_rs_value.o ./src/device/value/channel_temphum_value.d ./src/device/value/channel_temphum_value.o ./src/device/value/channel_value.d ./src/device/value/channel_value.o ./src/device/value/channel_value_envelope.d ./src/device/value/channel_value_envelope.o ./src/device/value/channel_value_factory.d ./src/device/value/channel_value_factory.o ./src/device/value/channel_valve_value.d ./src/device/value/channel_valve_value.o + -$(RM) ./src/device/value/abstract_channel_value.d ./src/device/value/abstract_channel_value.o ./src/device/value/abstract_rs_value.d ./src/device/value/abstract_rs_value.o ./src/device/value/channel_binary_sensor_value.d ./src/device/value/channel_binary_sensor_value.o ./src/device/value/channel_container_value.d ./src/device/value/channel_container_value.o ./src/device/value/channel_dgf_value.d ./src/device/value/channel_dgf_value.o ./src/device/value/channel_em_value.d ./src/device/value/channel_em_value.o ./src/device/value/channel_fb_value.d ./src/device/value/channel_fb_value.o ./src/device/value/channel_floating_point_sensor_value.d ./src/device/value/channel_floating_point_sensor_value.o ./src/device/value/channel_gate_value.d ./src/device/value/channel_gate_value.o ./src/device/value/channel_general_purpose_base_value.d ./src/device/value/channel_general_purpose_base_value.o ./src/device/value/channel_general_purpose_measurement_value.d ./src/device/value/channel_general_purpose_measurement_value.o ./src/device/value/channel_general_purpose_meter_value.d ./src/device/value/channel_general_purpose_meter_value.o ./src/device/value/channel_general_purpose_text_value.d ./src/device/value/channel_general_purpose_text_value.o ./src/device/value/channel_hp_thermostat_value.d ./src/device/value/channel_hp_thermostat_value.o ./src/device/value/channel_hvac_value.d ./src/device/value/channel_hvac_value.o ./src/device/value/channel_hvac_value_with_temphum.d ./src/device/value/channel_hvac_value_with_temphum.o ./src/device/value/channel_ic_value.d ./src/device/value/channel_ic_value.o ./src/device/value/channel_onoff_value.d ./src/device/value/channel_onoff_value.o ./src/device/value/channel_openclosed_value.d ./src/device/value/channel_openclosed_value.o ./src/device/value/channel_rgbw_value.d ./src/device/value/channel_rgbw_value.o ./src/device/value/channel_rs_value.d ./src/device/value/channel_rs_value.o ./src/device/value/channel_temphum_value.d ./src/device/value/channel_temphum_value.o ./src/device/value/channel_value.d ./src/device/value/channel_value.o ./src/device/value/channel_value_envelope.d ./src/device/value/channel_value_envelope.o ./src/device/value/channel_value_factory.d ./src/device/value/channel_value_factory.o ./src/device/value/channel_valve_value.d ./src/device/value/channel_valve_value.o .PHONY: clean-src-2f-device-2f-value diff --git a/supla-server/Test/src/ipc/subdir.mk b/supla-server/Test/src/ipc/subdir.mk index a43d5a1be2..a425a0297f 100644 --- a/supla-server/Test/src/ipc/subdir.mk +++ b/supla-server/Test/src/ipc/subdir.mk @@ -20,6 +20,7 @@ CPP_SRCS += \ ../src/ipc/abstract_get_double_command.cpp \ ../src/ipc/abstract_get_em_value_command.cpp \ ../src/ipc/abstract_get_facade_blind_value_command.cpp \ +../src/ipc/abstract_get_gpt_value_command.cpp \ ../src/ipc/abstract_get_gpm_value_command.cpp \ ../src/ipc/abstract_get_humidity_command.cpp \ ../src/ipc/abstract_get_hvac_value_command.cpp \ @@ -87,6 +88,7 @@ CPP_SRCS += \ ../src/ipc/get_double_command.cpp \ ../src/ipc/get_em_value_command.cpp \ ../src/ipc/get_facade_blind_value_command.cpp \ +../src/ipc/get_gpt_value_command.cpp \ ../src/ipc/get_gpm_value_command.cpp \ ../src/ipc/get_humidity_command.cpp \ ../src/ipc/get_hvac_value_command.cpp \ @@ -159,6 +161,7 @@ CPP_DEPS += \ ./src/ipc/abstract_get_double_command.d \ ./src/ipc/abstract_get_em_value_command.d \ ./src/ipc/abstract_get_facade_blind_value_command.d \ +./src/ipc/abstract_get_gpt_value_command.d \ ./src/ipc/abstract_get_gpm_value_command.d \ ./src/ipc/abstract_get_humidity_command.d \ ./src/ipc/abstract_get_hvac_value_command.d \ @@ -226,6 +229,7 @@ CPP_DEPS += \ ./src/ipc/get_double_command.d \ ./src/ipc/get_em_value_command.d \ ./src/ipc/get_facade_blind_value_command.d \ +./src/ipc/get_gpt_value_command.d \ ./src/ipc/get_gpm_value_command.d \ ./src/ipc/get_humidity_command.d \ ./src/ipc/get_hvac_value_command.d \ @@ -298,6 +302,7 @@ OBJS += \ ./src/ipc/abstract_get_double_command.o \ ./src/ipc/abstract_get_em_value_command.o \ ./src/ipc/abstract_get_facade_blind_value_command.o \ +./src/ipc/abstract_get_gpt_value_command.o \ ./src/ipc/abstract_get_gpm_value_command.o \ ./src/ipc/abstract_get_humidity_command.o \ ./src/ipc/abstract_get_hvac_value_command.o \ @@ -365,6 +370,7 @@ OBJS += \ ./src/ipc/get_double_command.o \ ./src/ipc/get_em_value_command.o \ ./src/ipc/get_facade_blind_value_command.o \ +./src/ipc/get_gpt_value_command.o \ ./src/ipc/get_gpm_value_command.o \ ./src/ipc/get_humidity_command.o \ ./src/ipc/get_hvac_value_command.o \ diff --git a/supla-server/src/cyclictasks/agent.cpp b/supla-server/src/cyclictasks/agent.cpp index 5a778f5b80..dd1d59bff1 100644 --- a/supla-server/src/cyclictasks/agent.cpp +++ b/supla-server/src/cyclictasks/agent.cpp @@ -27,6 +27,7 @@ #include "datalogger/current_logger.h" #include "datalogger/general_purpose_measurement_logger.h" #include "datalogger/general_purpose_meter_logger.h" +#include "datalogger/general_purpose_text_logger.h" #include "datalogger/hp_thermostat_logger.h" #include "datalogger/impulse_logger.h" #include "datalogger/power_active_logger.h" @@ -53,6 +54,7 @@ supla_cyclictasks_agent::supla_cyclictasks_agent() { add(new supla_auto_gate_closing()); add(new supla_general_purpose_measurement_logger()); add(new supla_general_purpose_meter_logger()); + add(new supla_general_purpose_text_logger()); add(new supla_virtual_channel_updater_cyclictask()); // Refreshing tokens at the end diff --git a/supla-server/src/datalogger/general_purpose_text_logger.cpp b/supla-server/src/datalogger/general_purpose_text_logger.cpp new file mode 100644 index 0000000000..83df43bd8f --- /dev/null +++ b/supla-server/src/datalogger/general_purpose_text_logger.cpp @@ -0,0 +1,75 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "datalogger/general_purpose_text_logger.h" + +#include +#include + +#include "datalogger/general_purpose_text_logger_dao.h" +#include "device/device.h" +#include "device/extended_value/channel_general_purpose_text_extended_value.h" +#include "user/user.h" + +using std::shared_ptr; +using std::vector; + +supla_general_purpose_text_logger::supla_general_purpose_text_logger() + : supla_abstract_cyclictask() {} + +supla_general_purpose_text_logger::~supla_general_purpose_text_logger() {} + +bool supla_general_purpose_text_logger::is_tsdb_preffered(void) { + return false; +} + +unsigned int supla_general_purpose_text_logger::task_interval_sec(void) { + return 60; +} + +void supla_general_purpose_text_logger::run( + const vector *users, supla_abstract_db_access_provider *dba) { + std::vector env; + + supla_general_purpose_text_logger_dao dao(dba); + + for (auto uit = users->cbegin(); uit != users->cend(); ++uit) { + (*uit)->get_devices()->for_each( + [&env](shared_ptr device, bool *will_continue) -> void { + device->get_channels()->get_channel_extended_values( + &env, + [](supla_abstract_channel_extended_value *value) -> bool { + return supla_channel_general_purpose_text_extended_value:: + is_ev_type_supported(value->get_type()); + }, + true); + }); + } + + for (auto it = env.cbegin(); it != env.cend(); ++it) { + supla_channel_general_purpose_text_extended_value *gpt_ev = + dynamic_cast( + (*it)->get_extended_value()); + + if (gpt_ev) { + dao.add((*it)->get_channel_id(), gpt_ev->get_text()); + } + + delete *it; + } +} diff --git a/supla-server/src/datalogger/general_purpose_text_logger.h b/supla-server/src/datalogger/general_purpose_text_logger.h new file mode 100644 index 0000000000..d587464b76 --- /dev/null +++ b/supla-server/src/datalogger/general_purpose_text_logger.h @@ -0,0 +1,38 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GENERAL_PURPOSE_TEXT_LOGGER_H_ +#define GENERAL_PURPOSE_TEXT_LOGGER_H_ + +#include + +#include "cyclictasks/abstract_cyclictask.h" + +class supla_general_purpose_text_logger : public supla_abstract_cyclictask { + protected: + virtual unsigned int task_interval_sec(void); + virtual void run(const std::vector *users, + supla_abstract_db_access_provider *dba); + + public: + supla_general_purpose_text_logger(); + virtual ~supla_general_purpose_text_logger(); + virtual bool is_tsdb_preffered(void); +}; + +#endif /* GENERAL_PURPOSE_TEXT_LOGGER_H_ */ diff --git a/supla-server/src/datalogger/general_purpose_text_logger_dao.cpp b/supla-server/src/datalogger/general_purpose_text_logger_dao.cpp new file mode 100644 index 0000000000..f8dbaf01cb --- /dev/null +++ b/supla-server/src/datalogger/general_purpose_text_logger_dao.cpp @@ -0,0 +1,66 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "datalogger/general_purpose_text_logger_dao.h" + +#include +#include + +#include + +#include "log.h" + +using std::string; + +supla_general_purpose_text_logger_dao::supla_general_purpose_text_logger_dao( + supla_abstract_db_access_provider *dba) + : supla_abstract_cyclictask_dao(dba) {} + +void supla_general_purpose_text_logger_dao::mariadb_add( + int channel_id, const string &value) { + if (value.size() > 255) { + return; + } + + MYSQL_BIND pbind[2] = {}; + + pbind[0].buffer_type = MYSQL_TYPE_LONG; + pbind[0].buffer = (char *)&channel_id; + + unsigned long value_len = (unsigned long)value.size(); + pbind[1].buffer_type = MYSQL_TYPE_STRING; + pbind[1].buffer = (char *)value.c_str(); + pbind[1].buffer_length = value_len; + pbind[1].length = &value_len; + + const char sql[] = + "INSERT INTO `supla_gp_text_log`(`channel_id`,`date`,`value`) " + "VALUES(?,NOW(),?)"; + + MYSQL_STMT *stmt = nullptr; + get_mdba()->stmt_execute((void **)&stmt, sql, pbind, 2, true); + + if (stmt != nullptr) mysql_stmt_close(stmt); +} + +void supla_general_purpose_text_logger_dao::add(int channel_id, + const string &value) { + if (get_mdba()) { + mariadb_add(channel_id, value); + } +} diff --git a/supla-server/src/datalogger/general_purpose_text_logger_dao.h b/supla-server/src/datalogger/general_purpose_text_logger_dao.h new file mode 100644 index 0000000000..64d02fecb6 --- /dev/null +++ b/supla-server/src/datalogger/general_purpose_text_logger_dao.h @@ -0,0 +1,37 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GENERAL_PURPOSE_TEXT_LOGGER_DAO_H_ +#define GENERAL_PURPOSE_TEXT_LOGGER_DAO_H_ + +#include + +#include "cyclictasks/abstract_cyclictask_dao.h" + +class supla_general_purpose_text_logger_dao + : public supla_abstract_cyclictask_dao { + private: + void mariadb_add(int channel_id, const std::string &value); + + public: + explicit supla_general_purpose_text_logger_dao( + supla_abstract_db_access_provider *dba); + void add(int channel_id, const std::string &value); +}; + +#endif /* GENERAL_PURPOSE_TEXT_LOGGER_DAO_H_ */ diff --git a/supla-server/src/device/devicechannel.cpp b/supla-server/src/device/devicechannel.cpp index 7b0f55f493..76b07d6be3 100644 --- a/supla-server/src/device/devicechannel.cpp +++ b/supla-server/src/device/devicechannel.cpp @@ -29,6 +29,7 @@ #include "device/device_dao.h" #include "device/extended_value/channel_em_extended_value.h" #include "device/extended_value/channel_extended_value_factory.h" +#include "device/extended_value/channel_general_purpose_text_extended_value.h" #include "device/value/channel_value_factory.h" #include "jsonconfig/channel/action_trigger_config.h" #include "jsonconfig/channel/electricity_meter_config.h" @@ -654,7 +655,8 @@ void supla_device_channel::on_value_changed( true); } - if (old_value && new_value) { + // GPT should be evaluated on extended values to preserve full text payload. + if (old_value && new_value && get_func() != SUPLA_CHANNELFNC_GENERAL_PURPOSE_TEXT) { get_device()->get_user()->get_value_based_triggers()->on_value_changed( supla_caller(ctChannel, get_id()), get_id(), old_value, new_value); } @@ -717,6 +719,16 @@ void supla_device_channel::set_extended_value( delete config; } + + supla_channel_general_purpose_text_extended_value *gpt = + dynamic_cast( + new_value); + if (gpt) { + if (logger_purpose_extended_value) { + delete logger_purpose_extended_value; + } + logger_purpose_extended_value = gpt->copy(); + } } extended_value = new_value; diff --git a/supla-server/src/device/extended_value/channel_extended_value_factory.cpp b/supla-server/src/device/extended_value/channel_extended_value_factory.cpp index d96a242d31..018d29cdf9 100644 --- a/supla-server/src/device/extended_value/channel_extended_value_factory.cpp +++ b/supla-server/src/device/extended_value/channel_extended_value_factory.cpp @@ -21,6 +21,7 @@ #include "device/extended_value/channel_and_timer_state_extended_value.h" #include "device/extended_value/channel_em_extended_value.h" #include "device/extended_value/channel_ic_extended_value.h" +#include "device/extended_value/channel_general_purpose_text_extended_value.h" #include "device/extended_value/channel_multi_extended_value.h" #include "device/extended_value/channel_state_extended_value.h" #include "device/extended_value/channel_thermostat_extended_value.h" @@ -50,6 +51,9 @@ supla_abstract_channel_extended_value_factory::new_value( } else if (supla_channel_multi_extended_value::is_ev_type_supported( ev->type)) { return new supla_channel_multi_extended_value(ev); + } else if (supla_channel_general_purpose_text_extended_value:: + is_ev_type_supported(ev->type)) { + return new supla_channel_general_purpose_text_extended_value(ev); } return nullptr; @@ -77,6 +81,9 @@ supla_abstract_channel_extended_value_factory::new_value( } else if (supla_channel_multi_extended_value::is_ev_type_supported( ev->type)) { return new supla_channel_multi_extended_value(ev); + } else if (supla_channel_general_purpose_text_extended_value:: + is_ev_type_supported(ev->type)) { + return new supla_channel_general_purpose_text_extended_value(ev); } return nullptr; diff --git a/supla-server/src/device/extended_value/channel_general_purpose_text_extended_value.cpp b/supla-server/src/device/extended_value/channel_general_purpose_text_extended_value.cpp new file mode 100644 index 0000000000..af0df0629a --- /dev/null +++ b/supla-server/src/device/extended_value/channel_general_purpose_text_extended_value.cpp @@ -0,0 +1,86 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "channel_general_purpose_text_extended_value.h" + +#include + +#include + +#include "proto.h" + +using std::string; + +supla_channel_general_purpose_text_extended_value:: + supla_channel_general_purpose_text_extended_value() + : supla_abstract_channel_extended_value() {} + +supla_channel_general_purpose_text_extended_value:: + supla_channel_general_purpose_text_extended_value( + const TSuplaChannelExtendedValue *value) + : supla_abstract_channel_extended_value(value) {} + +supla_channel_general_purpose_text_extended_value:: + ~supla_channel_general_purpose_text_extended_value(void) {} + +supla_abstract_channel_extended_value * +supla_channel_general_purpose_text_extended_value::copy(void) { // NOLINT + supla_channel_general_purpose_text_extended_value *result = + new supla_channel_general_purpose_text_extended_value(); + result->set_raw_value(get_value_ptr()); + return result; +} + +bool supla_channel_general_purpose_text_extended_value::get_vbt_text_value( + _vbt_var_name_e var_name, std::string *value) { + if (var_name != var_name_none || !value) { + return false; + } + + *value = get_text(); + return true; +} + +nlohmann::json supla_channel_general_purpose_text_extended_value:: +get_template_data(void) { + nlohmann::json result = supla_abstract_channel_extended_value:: + get_template_data(); + result["value"] = get_text(); + return result; +} + +string supla_channel_general_purpose_text_extended_value::get_text( + void) { + TSuplaChannelExtendedValue ev = {}; + if (!get_raw_value(&ev) || ev.size == 0) { + return ""; + } + + // Ensure null-termination within the allowed size + unsigned int len = ev.size < SUPLA_GENERAL_PURPOSE_TEXT_MAX_SIZE + ? ev.size + : SUPLA_GENERAL_PURPOSE_TEXT_MAX_SIZE - 1; + ev.value[len] = '\0'; + return string(ev.value, strnlen(ev.value, len + 1)); +} + +// static +bool supla_channel_general_purpose_text_extended_value::is_ev_type_supported( + char type) { + return type == EV_TYPE_GENERAL_PURPOSE_TEXT; +} diff --git a/supla-server/src/device/extended_value/channel_general_purpose_text_extended_value.h b/supla-server/src/device/extended_value/channel_general_purpose_text_extended_value.h new file mode 100644 index 0000000000..5d001cb0f8 --- /dev/null +++ b/supla-server/src/device/extended_value/channel_general_purpose_text_extended_value.h @@ -0,0 +1,42 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef CHANNEL_GENERAL_PURPOSE_TEXT_EXTENDED_VALUE_H_ +#define CHANNEL_GENERAL_PURPOSE_TEXT_EXTENDED_VALUE_H_ + +#include + +#include "device/extended_value/abstract_channel_extended_value.h" + +class supla_channel_general_purpose_text_extended_value + : public supla_abstract_channel_extended_value { + public: + supla_channel_general_purpose_text_extended_value(); + explicit supla_channel_general_purpose_text_extended_value( + const TSuplaChannelExtendedValue *value); + virtual ~supla_channel_general_purpose_text_extended_value(void); + virtual supla_abstract_channel_extended_value *copy(void); // NOLINT + + bool get_vbt_text_value(_vbt_var_name_e var_name, + std::string *value) override; + nlohmann::json get_template_data(void) override; + std::string get_text(void); + static bool is_ev_type_supported(char type); +}; + +#endif /*CHANNEL_GENERAL_PURPOSE_TEXT_EXTENDED_VALUE_H_*/ diff --git a/supla-server/src/device/value/channel_general_purpose_text_value.cpp b/supla-server/src/device/value/channel_general_purpose_text_value.cpp new file mode 100644 index 0000000000..8c1f2210b2 --- /dev/null +++ b/supla-server/src/device/value/channel_general_purpose_text_value.cpp @@ -0,0 +1,61 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "channel_general_purpose_text_value.h" + +#include + +#include "proto.h" + +supla_channel_general_purpose_text_value:: + supla_channel_general_purpose_text_value() + : supla_abstract_channel_value() {} + +supla_channel_general_purpose_text_value:: + supla_channel_general_purpose_text_value( + const char raw_value[SUPLA_CHANNELVALUE_SIZE]) + : supla_abstract_channel_value(raw_value) {} + +supla_abstract_channel_value *supla_channel_general_purpose_text_value::copy( + void) const { // NOLINT + return new supla_channel_general_purpose_text_value(raw_value); +} + +bool supla_channel_general_purpose_text_value::get_vbt_text_value( + _vbt_var_name_e var_name, std::string *value) { + if (var_name != var_name_none || !value) { + return false; + } + + *value = std::string(raw_value, strnlen(raw_value, SUPLA_CHANNELVALUE_SIZE)); + return true; +} + +nlohmann::json supla_channel_general_purpose_text_value::get_template_data( + void) { + nlohmann::json result = supla_vbt_value::get_template_data(); + std::string value(raw_value, strnlen(raw_value, SUPLA_CHANNELVALUE_SIZE)); + result["value"] = value; + return result; +} + +// static +bool supla_channel_general_purpose_text_value::is_function_supported( + int func) { + return func == SUPLA_CHANNELFNC_GENERAL_PURPOSE_TEXT; +} diff --git a/supla-server/src/device/value/channel_general_purpose_text_value.h b/supla-server/src/device/value/channel_general_purpose_text_value.h new file mode 100644 index 0000000000..196eab2b1f --- /dev/null +++ b/supla-server/src/device/value/channel_general_purpose_text_value.h @@ -0,0 +1,37 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef CHANNEL_GENERAL_PURPOSE_TEXT_VALUE_H_ +#define CHANNEL_GENERAL_PURPOSE_TEXT_VALUE_H_ + +#include "device/value/abstract_channel_value.h" + +class supla_channel_general_purpose_text_value + : public supla_abstract_channel_value { + public: + supla_channel_general_purpose_text_value(); + explicit supla_channel_general_purpose_text_value( + const char raw_value[SUPLA_CHANNELVALUE_SIZE]); + virtual supla_abstract_channel_value *copy(void) const; // NOLINT + bool get_vbt_text_value(_vbt_var_name_e var_name, + std::string *value) override; + nlohmann::json get_template_data(void) override; + static bool is_function_supported(int func); +}; + +#endif /*CHANNEL_GENERAL_PURPOSE_TEXT_VALUE_H_*/ diff --git a/supla-server/src/device/value/channel_value_factory.cpp b/supla-server/src/device/value/channel_value_factory.cpp index f994eaab76..08d77485f4 100644 --- a/supla-server/src/device/value/channel_value_factory.cpp +++ b/supla-server/src/device/value/channel_value_factory.cpp @@ -27,6 +27,7 @@ #include "device/value/channel_gate_value.h" #include "device/value/channel_general_purpose_measurement_value.h" #include "device/value/channel_general_purpose_meter_value.h" +#include "device/value/channel_general_purpose_text_value.h" #include "device/value/channel_hp_thermostat_value.h" #include "device/value/channel_hvac_value.h" #include "device/value/channel_ic_value.h" @@ -127,5 +128,9 @@ supla_abstract_channel_value *supla_abstract_channel_value_factory::new_value( return new supla_channel_general_purpose_meter_value(value); } + if (supla_channel_general_purpose_text_value::is_function_supported(func)) { + return new supla_channel_general_purpose_text_value(value); + } + return new supla_channel_value(value); } diff --git a/supla-server/src/ipc/abstract_get_gpt_value_command.cpp b/supla-server/src/ipc/abstract_get_gpt_value_command.cpp new file mode 100644 index 0000000000..1ca497be8e --- /dev/null +++ b/supla-server/src/ipc/abstract_get_gpt_value_command.cpp @@ -0,0 +1,48 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "abstract_get_gpt_value_command.h" + +#include + +#include "ipc/abstract_ipc_ctrl.h" + +using std::string; + +supla_abstract_get_gpt_value_command::supla_abstract_get_gpt_value_command( + supla_abstract_ipc_socket_adapter *socket_adapter) + : supla_abstract_ipc_command(socket_adapter) {} + +const string supla_abstract_get_gpt_value_command::get_command_name(void) { + return "GET-GPT-VALUE:"; +} + +void supla_abstract_get_gpt_value_command::on_command_match( + const char *params) { + process_parameters( + params, [this](int user_id, int device_id, int channel_id) -> bool { + string value; + if (!get_gpt_value(user_id, device_id, channel_id, &value)) { + return false; + } + + string result = "VALUE:" + value; + send_result(result.c_str()); + return true; + }); +} diff --git a/supla-server/src/ipc/abstract_get_gpt_value_command.h b/supla-server/src/ipc/abstract_get_gpt_value_command.h new file mode 100644 index 0000000000..88eef002f0 --- /dev/null +++ b/supla-server/src/ipc/abstract_get_gpt_value_command.h @@ -0,0 +1,38 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SUPLA_ABSTRACT_GET_GPT_VALUE_COMMAND_H_ +#define SUPLA_ABSTRACT_GET_GPT_VALUE_COMMAND_H_ + +#include + +#include "ipc/abstract_ipc_command.h" + +class supla_abstract_get_gpt_value_command : public supla_abstract_ipc_command { + protected: + virtual void on_command_match(const char *params); + virtual bool get_gpt_value(int user_id, int device_id, int channel_id, + std::string *value) = 0; + + public: + explicit supla_abstract_get_gpt_value_command( + supla_abstract_ipc_socket_adapter *socket_adapter); + virtual const std::string get_command_name(void); +}; + +#endif /* SUPLA_ABSTRACT_GET_GPT_VALUE_COMMAND_H_ */ diff --git a/supla-server/src/ipc/get_gpt_value_command.cpp b/supla-server/src/ipc/get_gpt_value_command.cpp new file mode 100644 index 0000000000..1d909554e1 --- /dev/null +++ b/supla-server/src/ipc/get_gpt_value_command.cpp @@ -0,0 +1,55 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "ipc/get_gpt_value_command.h" + +#include + +#include "device/channel_property_getter.h" +#include "device/extended_value/channel_general_purpose_text_extended_value.h" + +using std::string; + +supla_get_gpt_value_command::supla_get_gpt_value_command( + supla_abstract_ipc_socket_adapter *socket_adapter) + : supla_abstract_get_gpt_value_command(socket_adapter) {} + +bool supla_get_gpt_value_command::get_gpt_value(int user_id, int device_id, + int channel_id, + string *value) { + bool result = false; + + supla_channel_property_getter getter; + + supla_abstract_channel_extended_value *ev = + getter.get_extended_value(user_id, device_id, channel_id); + + if (ev) { + supla_channel_general_purpose_text_extended_value *gpt_ev = + dynamic_cast(ev); + + if (gpt_ev) { + *value = gpt_ev->get_text(); + result = true; + } + + delete ev; + } + + return result; +} diff --git a/supla-server/src/ipc/get_gpt_value_command.h b/supla-server/src/ipc/get_gpt_value_command.h new file mode 100644 index 0000000000..75cd816221 --- /dev/null +++ b/supla-server/src/ipc/get_gpt_value_command.h @@ -0,0 +1,36 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SUPLA_GET_GPT_VALUE_COMMAND_H_ +#define SUPLA_GET_GPT_VALUE_COMMAND_H_ + +#include + +#include "ipc/abstract_get_gpt_value_command.h" + +class supla_get_gpt_value_command : public supla_abstract_get_gpt_value_command { + protected: + virtual bool get_gpt_value(int user_id, int device_id, int channel_id, + std::string *value); + + public: + explicit supla_get_gpt_value_command( + supla_abstract_ipc_socket_adapter *socket_adapter); +}; + +#endif /* SUPLA_GET_GPT_VALUE_COMMAND_H_ */ diff --git a/supla-server/src/ipc/ipc_ctrl.cpp b/supla-server/src/ipc/ipc_ctrl.cpp index 79b0ae0acd..c4edce7282 100644 --- a/supla-server/src/ipc/ipc_ctrl.cpp +++ b/supla-server/src/ipc/ipc_ctrl.cpp @@ -38,6 +38,7 @@ #include "ipc/get_em_value_command.h" #include "ipc/get_facade_blind_value_command.h" #include "ipc/get_gpm_value_command.h" +#include "ipc/get_gpt_value_command.h" #include "ipc/get_humidity_command.h" #include "ipc/get_hvac_value_command.h" #include "ipc/get_ic_value_command.h" @@ -111,6 +112,7 @@ supla_ipc_ctrl::supla_ipc_ctrl( add_command(new supla_get_relay_value_command(socket_adapter)); add_command(new supla_get_hvac_value_command(socket_adapter)); add_command(new supla_get_gpm_value_command(socket_adapter)); + add_command(new supla_get_gpt_value_command(socket_adapter)); add_command(new supla_get_facade_blind_value_command(socket_adapter)); add_command(new supla_get_roller_shutter_value_command(socket_adapter)); add_command(new supla_set_char_command(socket_adapter)); diff --git a/supla-server/src/jsonconfig/channel/general_purpose_text_config.cpp b/supla-server/src/jsonconfig/channel/general_purpose_text_config.cpp new file mode 100644 index 0000000000..3fe52e435b --- /dev/null +++ b/supla-server/src/jsonconfig/channel/general_purpose_text_config.cpp @@ -0,0 +1,106 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "general_purpose_text_config.h" + +#include +#include + +using std::map; +using std::string; + +#define FIELD_KEEP_HISTORY 1 +#define FIELD_REFRESH_INTERVAL_MS 2 + +const map + general_purpose_text_config::field_map = { + {FIELD_KEEP_HISTORY, "keepHistory"}, + {FIELD_REFRESH_INTERVAL_MS, "refreshIntervalMs"}}; + +general_purpose_text_config::general_purpose_text_config( + supla_json_config *root) + : supla_json_config(root) {} + +general_purpose_text_config::general_purpose_text_config(void) + : supla_json_config() {} + +void general_purpose_text_config::merge(supla_json_config *_dst) { + general_purpose_text_config dst(_dst); + supla_json_config::merge(get_user_root(), dst.get_user_root(), field_map, + true); + supla_json_config::merge(get_properties_root(), dst.get_properties_root(), + field_map, true); +} + +void general_purpose_text_config::set_config( + TChannelConfig_GeneralPurposeText *config) { + if (!config) { + return; + } + + cJSON *user_root = get_user_root(); + if (!user_root) { + return; + } + + set_item_value(user_root, + field_map.at(FIELD_KEEP_HISTORY).c_str(), + cJSON_Number, true, nullptr, nullptr, + config->KeepHistory ? 1 : 0); + + set_item_value(user_root, + field_map.at(FIELD_REFRESH_INTERVAL_MS).c_str(), + cJSON_Number, true, nullptr, nullptr, + config->RefreshIntervalMs); +} + +bool general_purpose_text_config::get_config( + TChannelConfig_GeneralPurposeText *config) { + bool result = false; + + if (!config) { + return result; + } + + cJSON *user_root = get_user_root(); + if (!user_root) { + return result; + } + + double dbl = 0; + + if (get_double(user_root, field_map.at(FIELD_KEEP_HISTORY).c_str(), &dbl)) { + config->KeepHistory = dbl >= 1 ? 1 : 0; + result = true; + } + + if (get_double(user_root, + field_map.at(FIELD_REFRESH_INTERVAL_MS).c_str(), &dbl)) { + config->RefreshIntervalMs = + static_cast(dbl); + result = true; + } + + return result; +} + +bool general_purpose_text_config::keep_history(void) { + TChannelConfig_GeneralPurposeText config = {}; + get_config(&config); + return config.KeepHistory == 1; +} diff --git a/supla-server/src/jsonconfig/channel/general_purpose_text_config.h b/supla-server/src/jsonconfig/channel/general_purpose_text_config.h new file mode 100644 index 0000000000..7ef6d51efe --- /dev/null +++ b/supla-server/src/jsonconfig/channel/general_purpose_text_config.h @@ -0,0 +1,41 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GENERAL_PURPOSE_TEXT_CONFIG_H_ +#define GENERAL_PURPOSE_TEXT_CONFIG_H_ + +#include +#include + +#include "jsonconfig/json_config.h" +#include "proto.h" + +class general_purpose_text_config : public supla_json_config { + private: + static const std::map field_map; + + public: + explicit general_purpose_text_config(supla_json_config *root); + general_purpose_text_config(void); + virtual void merge(supla_json_config *dst); + void set_config(TChannelConfig_GeneralPurposeText *config); + bool get_config(TChannelConfig_GeneralPurposeText *config); + bool keep_history(void); +}; + +#endif /* GENERAL_PURPOSE_TEXT_CONFIG_H_ */ diff --git a/supla-server/src/mqtt/mqtt_abstract_state_message_provider.cpp b/supla-server/src/mqtt/mqtt_abstract_state_message_provider.cpp index 0dbb23c097..a328104068 100644 --- a/supla-server/src/mqtt/mqtt_abstract_state_message_provider.cpp +++ b/supla-server/src/mqtt/mqtt_abstract_state_message_provider.cpp @@ -25,6 +25,7 @@ #include #include "device/channel_fragment.h" +#include "device/extended_value/channel_general_purpose_text_extended_value.h" #include "device/extended_value/channel_hp_thermostat_ev_decorator.h" #include "device/extended_value/channel_ic_extended_value.h" #include "device/extended_value/channel_thermostat_extended_value.h" @@ -1140,6 +1141,30 @@ bool supla_mqtt_abstract_state_message_provider::get_gpm_message_at_index( return false; } +bool supla_mqtt_abstract_state_message_provider::get_gpt_message_at_index( + unsigned short index, const char *topic_prefix, char **topic_name, + void **message, size_t *message_size) { + if (index == 1) { + if (channel_extended_value == nullptr) { + channel_extended_value = + _get_channel_property_getter()->get_extended_value( + get_user_id(), get_device_id(), get_channel_id()); + } + + supla_channel_general_purpose_text_extended_value *gpt_val = + dynamic_cast( + channel_extended_value); + + return create_message( + topic_prefix, user_suid, topic_name, message, message_size, + gpt_val ? gpt_val->get_text().c_str() : nullptr, false, + "devices/%i/channels/%i/state/value", get_device_id(), + get_channel_id()); + } + + return false; +} + bool supla_mqtt_abstract_state_message_provider::get_message_at_index( unsigned short index, const char *topic_prefix, char **topic_name, void **message, size_t *message_size) { @@ -1385,7 +1410,10 @@ bool supla_mqtt_abstract_state_message_provider::get_message_at_index( case SUPLA_CHANNELFNC_GENERAL_PURPOSE_MEASUREMENT: return get_gpm_message_at_index(index, topic_prefix, topic_name, message, message_size); - break; + + case SUPLA_CHANNELFNC_GENERAL_PURPOSE_TEXT: + return get_gpt_message_at_index(index, topic_prefix, topic_name, message, + message_size); } return false; diff --git a/supla-server/src/mqtt/mqtt_abstract_state_message_provider.h b/supla-server/src/mqtt/mqtt_abstract_state_message_provider.h index 8a224ecd5f..5d63220080 100644 --- a/supla-server/src/mqtt/mqtt_abstract_state_message_provider.h +++ b/supla-server/src/mqtt/mqtt_abstract_state_message_provider.h @@ -165,6 +165,10 @@ class supla_mqtt_abstract_state_message_provider char **topic_name, void **message, size_t *message_size); + bool get_gpt_message_at_index(unsigned short index, const char *topic_prefix, + char **topic_name, void **message, + size_t *message_size); + public: supla_mqtt_abstract_state_message_provider(void); virtual ~supla_mqtt_abstract_state_message_provider(void); diff --git a/supla-server/src/push/push_notification.cpp b/supla-server/src/push/push_notification.cpp index 8b4a6fc5f8..d6bd689c78 100644 --- a/supla-server/src/push/push_notification.cpp +++ b/supla-server/src/push/push_notification.cpp @@ -129,7 +129,8 @@ string supla_push_notification::apply_template_data(string str) { pattern.append("}"); size_t pos = 0; - string v = supla_json_helper::to_string(value); + string v = value.is_string() ? value.get() + : supla_json_helper::to_string(value); while ((pos = str.find(pattern, pos)) != string::npos) { str.replace(pos, pattern.length(), v); diff --git a/supla-server/src/test/integration/push/PushNotificationIntegrationTest.cpp b/supla-server/src/test/integration/push/PushNotificationIntegrationTest.cpp index 7c9064dd33..c5337104dd 100644 --- a/supla-server/src/test/integration/push/PushNotificationIntegrationTest.cpp +++ b/supla-server/src/test/integration/push/PushNotificationIntegrationTest.cpp @@ -18,8 +18,10 @@ #include "PushNotificationIntegrationTest.h" +#include #include +#include "device/value/channel_general_purpose_text_value.h" #include "push/push_notification.h" using std::string; @@ -108,4 +110,19 @@ TEST_F(PushNotificationIntegrationTest, applyJsonWithId) { EXPECT_EQ(p.get_recipients().total_count(), 0); } +TEST_F(PushNotificationIntegrationTest, + generalPurposeTextTemplateUsesLowercaseValueOnly) { + char raw_value[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(raw_value, "TextValue", SUPLA_CHANNELVALUE_SIZE - 1); + + supla_channel_general_purpose_text_value value(raw_value); + auto d = value.get_template_data(); + + supla_push_notification p; + p.set_body("{value}|{{value}}|{Value}"); + p.set_template_data(&d); + + EXPECT_EQ(p.get_body(), "TextValue|TextValue|{Value}"); +} + } /* namespace testing */ diff --git a/supla-server/src/test/integration/vbt/DelayedValueBasedTriggerIntegrationTest.cpp b/supla-server/src/test/integration/vbt/DelayedValueBasedTriggerIntegrationTest.cpp index e74f46e7b2..7ee6056e77 100644 --- a/supla-server/src/test/integration/vbt/DelayedValueBasedTriggerIntegrationTest.cpp +++ b/supla-server/src/test/integration/vbt/DelayedValueBasedTriggerIntegrationTest.cpp @@ -22,6 +22,7 @@ #include #include +#include "device/value/channel_general_purpose_text_value.h" #include "device/value/channel_onoff_value.h" #include "log.h" @@ -277,4 +278,128 @@ TEST_F(DelayedValueBasedTriggerIntegrationTest, multipleActions) { EXPECT_LT(ms, 1400); } +TEST_F(DelayedValueBasedTriggerIntegrationTest, + generalPurposeTextPushNotificationAfterDuration) { + runSqlScript("AddDelayedGeneralPurposeTextValueBasedTrigger.sql"); + triggers->load(); + + auto gpt_trigger = triggers->get(34); + ASSERT_TRUE(gpt_trigger != nullptr); + EXPECT_EQ(gpt_trigger->get_channel_id(), 320); + EXPECT_EQ(gpt_trigger->get_action_config().get_subject_type(), + stPushNotification); + EXPECT_EQ(gpt_trigger->get_action_config().get_subject_id(), 500); + EXPECT_EQ(gpt_trigger->get_action_config().get_action_id(), ACTION_SEND); + EXPECT_EQ(gpt_trigger->get_on_change_cnd().get_op(), op_eq); + EXPECT_EQ(gpt_trigger->get_on_change_cnd().get_duration_sec(), 1); + double latitude = 0; + double longitude = 0; + std::string timezone = user->get_timezone(&latitude, &longitude); + EXPECT_TRUE(gpt_trigger->get_active_period().is_now_active( + timezone.c_str(), latitude, longitude)); + + char old_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(old_raw, "before", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value oldv(old_raw); + + char match_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(match_raw, "match", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value matchv(match_raw); + + auto direct_match = gpt_trigger->is_condition_met(320, &oldv, &matchv); + _supla_int64_t direct_ms_left = 0; + EXPECT_TRUE(direct_match.is_condition_met(&direct_ms_left)); + EXPECT_GT(direct_ms_left, 0); + + auto start = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point fire; + + EXPECT_CALL(*actionExecutor, send(_, _)) + .WillOnce([&](const supla_caller &caller, nlohmann::json *template_data) { + fire = std::chrono::steady_clock::now(); + EXPECT_EQ(actionExecutor->get_push_notification_id(), 500); + ASSERT_NE(template_data, nullptr); + EXPECT_EQ((*template_data)["value"].get(), "match"); + }); + + triggers->on_value_changed(supla_caller(ctIPC), 320, &oldv, &matchv, + actionExecutor, propertyGetter); + + usleep(1500000); + + auto ms = std::chrono::duration_cast(fire - start) + .count(); + EXPECT_GT(ms, 1000); + EXPECT_LT(ms, 1500); +} + +TEST_F(DelayedValueBasedTriggerIntegrationTest, + generalPurposeTextPushNotificationResetsWhenValueChangesAway) { + runSqlScript("AddDelayedGeneralPurposeTextValueBasedTrigger.sql"); + triggers->load(); + + auto gpt_trigger = triggers->get(34); + ASSERT_TRUE(gpt_trigger != nullptr); + EXPECT_EQ(gpt_trigger->get_channel_id(), 320); + EXPECT_EQ(gpt_trigger->get_action_config().get_subject_type(), + stPushNotification); + EXPECT_EQ(gpt_trigger->get_action_config().get_subject_id(), 500); + EXPECT_EQ(gpt_trigger->get_action_config().get_action_id(), ACTION_SEND); + EXPECT_EQ(gpt_trigger->get_on_change_cnd().get_op(), op_eq); + EXPECT_EQ(gpt_trigger->get_on_change_cnd().get_duration_sec(), 1); + double latitude = 0; + double longitude = 0; + std::string timezone = user->get_timezone(&latitude, &longitude); + EXPECT_TRUE(gpt_trigger->get_active_period().is_now_active( + timezone.c_str(), latitude, longitude)); + + char old_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(old_raw, "before", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value oldv(old_raw); + + char match_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(match_raw, "match", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value matchv(match_raw); + + char other_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(other_raw, "other", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value otherv(other_raw); + + auto direct_match = gpt_trigger->is_condition_met(320, &oldv, &matchv); + _supla_int64_t direct_ms_left = 0; + EXPECT_TRUE(direct_match.is_condition_met(&direct_ms_left)); + EXPECT_GT(direct_ms_left, 0); + + auto start = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point fire; + + EXPECT_CALL(*actionExecutor, send(_, _)) + .WillOnce([&](const supla_caller &caller, nlohmann::json *template_data) { + fire = std::chrono::steady_clock::now(); + EXPECT_EQ(actionExecutor->get_push_notification_id(), 500); + ASSERT_NE(template_data, nullptr); + EXPECT_EQ((*template_data)["value"].get(), "match"); + }); + + triggers->on_value_changed(supla_caller(ctIPC), 320, &oldv, &matchv, + actionExecutor, propertyGetter); + + usleep(500000); + + triggers->on_value_changed(supla_caller(ctIPC), 320, &matchv, &otherv, + actionExecutor, propertyGetter); + + usleep(700000); + + triggers->on_value_changed(supla_caller(ctIPC), 320, &otherv, &matchv, + actionExecutor, propertyGetter); + + usleep(1100000); + + auto ms = std::chrono::duration_cast(fire - start) + .count(); + EXPECT_GT(ms, 2100); + EXPECT_LT(ms, 2600); +} + } /* namespace testing */ diff --git a/supla-server/src/test/integration/vbt/ValueBasedTriggerIntegrationTest.cpp b/supla-server/src/test/integration/vbt/ValueBasedTriggerIntegrationTest.cpp index d41783b11b..ccd57ea5c3 100644 --- a/supla-server/src/test/integration/vbt/ValueBasedTriggerIntegrationTest.cpp +++ b/supla-server/src/test/integration/vbt/ValueBasedTriggerIntegrationTest.cpp @@ -22,6 +22,7 @@ #include #include "actions/action_shading_system_parameters.h" +#include "device/value/channel_general_purpose_text_value.h" #include "device/value/channel_onoff_value.h" #include "device/value/channel_temphum_value.h" #include "doubles/actions/ActionExecutorMock.h" @@ -402,4 +403,57 @@ TEST_F(ValueBasedTriggerIntegrationTest, fireForChannel158) { EXPECT_EQ(m["humidity"].get(), 50.00); } +TEST_F(ValueBasedTriggerIntegrationTest, + fireForGeneralPurposeTextChannelWithPushNotification) { + runSqlScript("AddGeneralPurposeTextValueBasedTrigger.sql"); + triggers->load(); + + auto gpt_trigger = triggers->get(34); + ASSERT_TRUE(gpt_trigger != nullptr); + EXPECT_EQ(gpt_trigger->get_channel_id(), 320); + EXPECT_EQ(gpt_trigger->get_action_config().get_subject_type(), + stPushNotification); + EXPECT_EQ(gpt_trigger->get_action_config().get_subject_id(), 500); + EXPECT_EQ(gpt_trigger->get_action_config().get_action_id(), ACTION_SEND); + EXPECT_EQ(gpt_trigger->get_on_change_cnd().get_op(), op_eq); + EXPECT_EQ(gpt_trigger->get_on_change_cnd().get_duration_sec(), 0); + double latitude = 0; + double longitude = 0; + std::string timezone = user->get_timezone(&latitude, &longitude); + EXPECT_TRUE(gpt_trigger->get_active_period().is_now_active( + timezone.c_str(), latitude, longitude)); + + char old_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(old_raw, "before", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value oldv(old_raw); + + char wrong_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(wrong_raw, "wrong", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value wrongv(wrong_raw); + + char match_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(match_raw, "match", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value matchv(match_raw); + + ActionExecutorMock actionExecutor; + ChannelPropertyGetterMock propertyGetter; + + triggers->on_value_changed(supla_caller(ctIPC), 320, &oldv, &wrongv, + &actionExecutor, &propertyGetter); + EXPECT_EQ(actionExecutor.getSentCounter(), 0); + + auto direct_match = gpt_trigger->is_condition_met(320, &wrongv, &matchv); + EXPECT_TRUE(direct_match.is_condition_met()); + + triggers->on_value_changed(supla_caller(ctIPC), 320, &wrongv, &matchv, + &actionExecutor, &propertyGetter); + + EXPECT_EQ(actionExecutor.get_push_notification_id(), 500); + EXPECT_EQ(actionExecutor.getSentCounter(), 1); + + auto m = actionExecutor.get_template_data(); + EXPECT_EQ(m.size(), 1); + EXPECT_EQ(m["value"].get(), "match"); +} + } /* namespace testing */ diff --git a/supla-server/src/test/push/PushNotificationTest.cpp b/supla-server/src/test/push/PushNotificationTest.cpp index d2c69b7c0c..77f47898ee 100644 --- a/supla-server/src/test/push/PushNotificationTest.cpp +++ b/supla-server/src/test/push/PushNotificationTest.cpp @@ -20,7 +20,9 @@ #include #include +#include +#include "device/value/channel_general_purpose_text_value.h" #include "push/push_notification.h" namespace testing { @@ -275,6 +277,20 @@ TEST_F(PushNotificationTest, injaAndNativeReplacement) { EXPECT_EQ(n.get_localized_body(), "abcd|abcd"); } +TEST_F(PushNotificationTest, generalPurposeTextTemplateUsesInjaValuePlaceholder) { + char raw_value[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(raw_value, "TextValue", SUPLA_CHANNELVALUE_SIZE - 1); + + supla_channel_general_purpose_text_value value(raw_value); + auto d = value.get_template_data(); + + supla_push_notification n; + n.set_body("{value}|{{value}}|{Value}"); + n.set_template_data(&d); + + EXPECT_EQ(n.get_body(), "TextValue|TextValue|{Value}"); +} + TEST_F(PushNotificationTest, injaException) { supla_push_notification n; n.set_body("{% non_existent_func }}"); diff --git a/supla-server/src/test/vbt/OnChangeConditionTest.cpp b/supla-server/src/test/vbt/OnChangeConditionTest.cpp index 5ba3ad6cac..bcb21e182e 100644 --- a/supla-server/src/test/vbt/OnChangeConditionTest.cpp +++ b/supla-server/src/test/vbt/OnChangeConditionTest.cpp @@ -20,14 +20,17 @@ #include #include +#include #include #include "device/channel_state.h" #include "device/extended_value/channel_em_extended_value.h" +#include "device/extended_value/channel_general_purpose_text_extended_value.h" #include "device/extended_value/channel_ic_extended_value.h" #include "device/value/channel_binary_sensor_value.h" #include "device/value/channel_container_value.h" #include "device/value/channel_floating_point_sensor_value.h" +#include "device/value/channel_general_purpose_text_value.h" #include "device/value/channel_onoff_value.h" #include "device/value/channel_rgbw_value.h" #include "device/value/channel_rs_value.h" @@ -114,6 +117,170 @@ TEST_F(OnChangeConditionTest, allPredictedOperators) { EXPECT_EQ(c.get_op(), op_gt); } +TEST_F(OnChangeConditionTest, generalPurposeTextEqualCondition) { + supla_vbt_on_change_condition c; + + cJSON *json = cJSON_Parse("{\"on_change_to\":{\"eq\":\"match\"}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + char old_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(old_raw, "before", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value old_value(old_raw); + + char new_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(new_raw, "match", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value new_value(new_raw); + + EXPECT_TRUE(c.is_condition_met(&old_value, &new_value)); +} + +TEST_F(OnChangeConditionTest, generalPurposeTextOnChangeCondition) { + supla_vbt_on_change_condition c; + + cJSON *json = cJSON_Parse("{\"on_change\":{}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + TSuplaChannelExtendedValue old_ev = {}; + old_ev.type = EV_TYPE_GENERAL_PURPOSE_TEXT; + old_ev.size = 3; + strncpy(old_ev.value, "old", sizeof(old_ev.value) - 1); + supla_channel_general_purpose_text_extended_value old_value(&old_ev); + + TSuplaChannelExtendedValue new_ev = {}; + new_ev.type = EV_TYPE_GENERAL_PURPOSE_TEXT; + new_ev.size = 3; + strncpy(new_ev.value, "new", sizeof(new_ev.value) - 1); + supla_channel_general_purpose_text_extended_value new_value(&new_ev); + + EXPECT_TRUE(c.is_condition_met(&old_value, &new_value)); +} + +TEST_F(OnChangeConditionTest, generalPurposeTextOnChangeDelayedCondition) { + supla_vbt_on_change_condition c; + + cJSON *json = cJSON_Parse("{\"on_change\":{\"duration_sec\":1}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + TSuplaChannelExtendedValue old_ev = {}; + old_ev.type = EV_TYPE_GENERAL_PURPOSE_TEXT; + old_ev.size = 3; + strncpy(old_ev.value, "old", sizeof(old_ev.value) - 1); + supla_channel_general_purpose_text_extended_value old_value(&old_ev); + + TSuplaChannelExtendedValue new_ev = {}; + new_ev.type = EV_TYPE_GENERAL_PURPOSE_TEXT; + new_ev.size = 3; + strncpy(new_ev.value, "new", sizeof(new_ev.value) - 1); + supla_channel_general_purpose_text_extended_value new_value(&new_ev); + + _supla_int64_t milliseconds_left = 0; + EXPECT_TRUE(c.is_condition_met(&old_value, &new_value, &milliseconds_left)); + EXPECT_GT(milliseconds_left, 0); + + usleep(1100000); + + milliseconds_left = 0; + EXPECT_TRUE(c.is_condition_met(&milliseconds_left)); + EXPECT_LE(milliseconds_left, 0); + EXPECT_FALSE(c.is_condition_met(&milliseconds_left)); +} + +TEST_F(OnChangeConditionTest, + generalPurposeTextOnChangeDelayedConditionRespectsTimeAfterNextChange) { + supla_vbt_on_change_condition c; + + cJSON *json = cJSON_Parse("{\"on_change\":{\"duration_sec\":1}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + char old_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(old_raw, "A", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value old_value(old_raw); + + char new_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(new_raw, "B", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value new_value(new_raw); + + _supla_int64_t milliseconds_left = 0; + EXPECT_TRUE(c.is_condition_met(&old_value, &new_value, &milliseconds_left)); + EXPECT_GT(milliseconds_left, 0); + + usleep(600000); + + char newer_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(newer_raw, "C", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value newer_value(newer_raw); + + milliseconds_left = 0; + EXPECT_TRUE(c.is_condition_met(&new_value, &newer_value, &milliseconds_left)); + EXPECT_GT(milliseconds_left, 0); + + usleep(600000); + milliseconds_left = 0; + EXPECT_FALSE(c.is_condition_met(&milliseconds_left)); + EXPECT_GT(milliseconds_left, 0); + + usleep(500000); + milliseconds_left = 0; + EXPECT_TRUE(c.is_condition_met(&milliseconds_left)); + EXPECT_LE(milliseconds_left, 0); +} + +TEST_F(OnChangeConditionTest, + generalPurposeTextEqualDelayedConditionRespectsValueAndTime) { + supla_vbt_on_change_condition c; + + cJSON *json = + cJSON_Parse("{\"on_change_to\":{\"eq\":\"match\",\"duration_sec\":1}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + char old_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(old_raw, "before", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value old_value(old_raw); + + char wrong_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(wrong_raw, "wrong", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value wrong_value(wrong_raw); + + _supla_int64_t milliseconds_left = 0; + EXPECT_FALSE(c.is_condition_met(&old_value, &wrong_value, &milliseconds_left)); + + char match_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(match_raw, "match", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value match_value(match_raw); + + milliseconds_left = 0; + EXPECT_TRUE(c.is_condition_met(&old_value, &match_value, &milliseconds_left)); + EXPECT_GT(milliseconds_left, 0); + + usleep(500000); + + char other_raw[SUPLA_CHANNELVALUE_SIZE] = {}; + strncpy(other_raw, "other", SUPLA_CHANNELVALUE_SIZE - 1); + supla_channel_general_purpose_text_value other_value(other_raw); + + milliseconds_left = 0; + EXPECT_FALSE( + c.is_condition_met(&match_value, &other_value, &milliseconds_left)); + + usleep(700000); + milliseconds_left = 0; + EXPECT_FALSE(c.is_condition_met(&milliseconds_left)); + + milliseconds_left = 0; + EXPECT_TRUE(c.is_condition_met(&other_value, &match_value, &milliseconds_left)); + EXPECT_GT(milliseconds_left, 0); + + usleep(1100000); + milliseconds_left = 0; + EXPECT_TRUE(c.is_condition_met(&milliseconds_left)); + EXPECT_LE(milliseconds_left, 0); +} + TEST_F(OnChangeConditionTest, onChangeTo_allPredictedVarNames) { supla_vbt_on_change_condition c; @@ -2550,4 +2717,112 @@ TEST_F(OnChangeConditionTest, containerWithInvalidSensorState) { } } +TEST_F(OnChangeConditionTest, boolValues_closedAndOpenAliases) { + supla_vbt_on_change_condition c; + + cJSON *json = cJSON_Parse("{\"on_change_to\":{\"eq\":\"closed\"}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + EXPECT_EQ(c.get_op(), op_eq); + EXPECT_EQ(c.get_value(), 1); + + json = cJSON_Parse("{\"on_change_to\":{\"eq\":\"open\"}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + EXPECT_EQ(c.get_op(), op_eq); + EXPECT_EQ(c.get_value(), 0); +} + +TEST_F(OnChangeConditionTest, invalidStringOperatorResetsCondition) { + supla_vbt_on_change_condition c; + + cJSON *json = cJSON_Parse("{\"on_change_to\":{\"gt\":\"match\"}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + EXPECT_EQ(c.get_op(), op_unknown); + + supla_channel_floating_point_sensor_value oldv(SUPLA_CHANNELFNC_WEIGHTSENSOR), + newv(SUPLA_CHANNELFNC_WEIGHTSENSOR); + oldv.set_value(1); + newv.set_value(2); + + EXPECT_FALSE(c.is_condition_met(&oldv, &newv)); +} + +TEST_F(OnChangeConditionTest, invalidResumeConfigResetsCondition) { + supla_vbt_on_change_condition c; + + cJSON *json = + cJSON_Parse("{\"on_change_to\":{\"eq\":1,\"resume\":{\"eq\":\"match\"}}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + EXPECT_EQ(c.get_op(), op_unknown); + + json = cJSON_Parse("{\"on_change_to\":{\"eq\":1,\"resume\":{}}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + EXPECT_EQ(c.get_op(), op_unknown); +} + +TEST_F(OnChangeConditionTest, missingConditionConfigReturnsFalse) { + supla_vbt_on_change_condition c; + + cJSON *json = cJSON_Parse("{}"); + c.apply_json_config(json); + cJSON_Delete(json); + + supla_channel_floating_point_sensor_value oldv(SUPLA_CHANNELFNC_WEIGHTSENSOR), + newv(SUPLA_CHANNELFNC_WEIGHTSENSOR); + oldv.set_value(1); + newv.set_value(2); + + EXPECT_FALSE(c.is_condition_met(&oldv, &newv)); +} + +TEST_F(OnChangeConditionTest, nullValuesReturnFalse) { + supla_vbt_on_change_condition c; + + cJSON *json = cJSON_Parse("{\"on_change\":{}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + supla_channel_floating_point_sensor_value value(SUPLA_CHANNELFNC_WEIGHTSENSOR); + value.set_value(1); + + EXPECT_FALSE(c.is_condition_met(nullptr, &value)); + EXPECT_FALSE(c.is_condition_met(&value, nullptr)); + EXPECT_FALSE(c.is_condition_met(nullptr, nullptr)); +} + +TEST_F(OnChangeConditionTest, delayedOnChangeIsCanceledWhenValueStopsChanging) { + supla_vbt_on_change_condition c; + + cJSON *json = cJSON_Parse("{\"on_change\":{\"duration_sec\":1}}"); + c.apply_json_config(json); + cJSON_Delete(json); + + supla_channel_floating_point_sensor_value oldv(SUPLA_CHANNELFNC_WEIGHTSENSOR), + newv(SUPLA_CHANNELFNC_WEIGHTSENSOR); + oldv.set_value(1); + newv.set_value(2); + + _supla_int64_t milliseconds_left = 0; + EXPECT_TRUE(c.is_condition_met(&oldv, &newv, &milliseconds_left)); + EXPECT_GT(milliseconds_left, 0); + + oldv.set_value(2); + newv.set_value(2); + EXPECT_FALSE(c.is_condition_met(&oldv, &newv, &milliseconds_left)); + EXPECT_EQ(milliseconds_left, 0); + + milliseconds_left = -1; + EXPECT_FALSE(c.is_condition_met(&milliseconds_left)); + EXPECT_EQ(milliseconds_left, 0); +} + } // namespace testing diff --git a/supla-server/src/tsdb/tsdb_access_provider.cpp b/supla-server/src/tsdb/tsdb_access_provider.cpp index bd3a4cc154..a08a7b83a3 100644 --- a/supla-server/src/tsdb/tsdb_access_provider.cpp +++ b/supla-server/src/tsdb/tsdb_access_provider.cpp @@ -190,7 +190,7 @@ string supla_tsdb_access_provider::time_to_timestamp_string(const time_t& t) { bool supla_tsdb_access_provider::check_db_version(void) { string found; - string expected = "20250625101351"; + string expected = "20260101000000"; bool _is_connected = is_connected(); if (!_is_connected && !connect()) { diff --git a/supla-server/src/vbt/vbt_on_change_condition.cpp b/supla-server/src/vbt/vbt_on_change_condition.cpp index c16de7a8b1..8dbe7f3885 100644 --- a/supla-server/src/vbt/vbt_on_change_condition.cpp +++ b/supla-server/src/vbt/vbt_on_change_condition.cpp @@ -28,14 +28,17 @@ using std::string; supla_vbt_on_change_condition::supla_vbt_on_change_condition(void) { value = 0; + text_value.clear(); var_name = var_name_none; op = op_unknown; resume_op = op_unknown; resume_value = 0; paused = false; on_change = false; + text_comparison = false; duration_sec = 0; saved_old_value = 0; + saved_old_text.clear(); condition_met_at = {}; } @@ -65,6 +68,9 @@ bool supla_vbt_on_change_condition::is_paused(void) const { return paused; } void supla_vbt_on_change_condition::apply_json_config(cJSON *json) { op = op_unknown; + text_value.clear(); + text_comparison = false; + saved_old_text.clear(); cJSON *root = cJSON_GetObjectItem(json, "on_change"); on_change = root != nullptr; @@ -158,14 +164,24 @@ void supla_vbt_on_change_condition::apply_json_config(cJSON *json) { return; } - if (!get_operator_and_value(root, &op, &value)) { + if (!get_operator_and_value(root, &op, &value, &text_value, + &text_comparison)) { op = op_unknown; return; } cJSON *resume_json = cJSON_GetObjectItem(root, "resume"); + std::string resume_text_value; + bool resume_text_comparison = false; if (resume_json && - !get_operator_and_value(resume_json, &resume_op, &resume_value)) { + !get_operator_and_value(resume_json, &resume_op, &resume_value, + &resume_text_value, + &resume_text_comparison)) { + op = op_unknown; + return; + } + + if (resume_text_comparison) { op = op_unknown; return; } @@ -173,7 +189,9 @@ void supla_vbt_on_change_condition::apply_json_config(cJSON *json) { bool supla_vbt_on_change_condition::get_operator_and_value(cJSON *root, _vbt_operator_e *op, - double *value) { + double *value, + std::string *text_value, + bool *text_comparison) { map<_vbt_operator_e, string> ops{{op_eq, "eq"}, {op_ne, "ne"}, {op_gt, "gt"}, {op_ge, "ge"}, {op_lt, "lt"}, {op_le, "le"}}; @@ -193,8 +211,10 @@ bool supla_vbt_on_change_condition::get_operator_and_value(cJSON *root, if (cJSON_IsNumber(op_json)) { *value = op_json->valuedouble; + *text_comparison = false; } else if (cJSON_IsBool(op_json)) { *value = cJSON_IsTrue(op_json) ? 1 : 0; + *text_comparison = false; } else if (cJSON_IsString(op_json)) { if (*op != op_eq && *op != op_ne) { return false; @@ -203,12 +223,17 @@ bool supla_vbt_on_change_condition::get_operator_and_value(cJSON *root, string value_str = cJSON_GetStringValue(op_json); if (value_str == "hi" || value_str == "closed" || value_str == "on") { *value = 1; + *text_comparison = false; } else if (value_str == "lo" || value_str == "low" || value_str == "open" || value_str == "off") { *value = 0; + *text_comparison = false; } else { - return false; + *text_value = value_str; + *text_comparison = true; } + } else { + return false; } return true; @@ -230,6 +255,17 @@ bool supla_vbt_on_change_condition::is_condition_met(_vbt_operator_e op, (op == op_le && new_value <= expected && old_value > expected); } +bool supla_vbt_on_change_condition::is_condition_met( + _vbt_operator_e op, const std::string &old_value, + const std::string &new_value, const std::string &expected) { + if (old_value == new_value) { + return false; + } + + return (op == op_eq && new_value == expected) || + (op == op_ne && new_value != expected); +} + bool supla_vbt_on_change_condition::is_condition_met( double old_value, double new_value, _supla_int64_t *milliseconds_left) { if (milliseconds_left) { @@ -237,7 +273,26 @@ bool supla_vbt_on_change_condition::is_condition_met( } if (on_change) { - return fabs(new_value - old_value) > 0.000001; + if (fabs(new_value - old_value) > 0.000001) { + if (duration_sec > 0) { + struct timeval now; + gettimeofday(&now, nullptr); + condition_met_at = now; + condition_met_at.tv_sec += duration_sec; + + if (milliseconds_left) { + *milliseconds_left = ms_left(now); + } + + return true; + } + + condition_met_at = {}; + return true; + } + + condition_met_at = {}; + return false; } if (paused) { @@ -271,6 +326,62 @@ bool supla_vbt_on_change_condition::is_condition_met( return false; } +bool supla_vbt_on_change_condition::is_condition_met( + const std::string &old_value, const std::string &new_value, + _supla_int64_t *milliseconds_left) { + if (milliseconds_left) { + *milliseconds_left = 0; + } + + if (on_change) { + if (old_value != new_value) { + if (duration_sec > 0) { + struct timeval now; + gettimeofday(&now, nullptr); + condition_met_at = now; + condition_met_at.tv_sec += duration_sec; + + if (milliseconds_left) { + *milliseconds_left = ms_left(now); + } + + return true; + } + + condition_met_at = {}; + return true; + } + + condition_met_at = {}; + return false; + } + + if (paused) { + return false; + } else if (is_condition_met(op, condition_met_at.tv_sec ? saved_old_text : old_value, + new_value, text_value)) { + struct timeval now; + gettimeofday(&now, nullptr); + + if (duration_sec > 0) { + if (!condition_met_at.tv_sec) { + saved_old_text = old_value; + condition_met_at = now; + condition_met_at.tv_sec += duration_sec; + } + + if (milliseconds_left) { + *milliseconds_left = ms_left(now); + } + } + + return true; + } + + condition_met_at = {}; + return false; +} + bool supla_vbt_on_change_condition::is_condition_met( supla_vbt_value *old_value, supla_vbt_value *new_value, _supla_int64_t *milliseconds_left) { @@ -278,15 +389,25 @@ bool supla_vbt_on_change_condition::is_condition_met( return false; } + if (!old_value || !new_value) { + return false; + } + double oldv = 0; double newv = 0; - if (!old_value || !old_value->get_vbt_value(var_name, &oldv)) { - return false; - } + bool old_numeric = old_value->get_vbt_value(var_name, &oldv); + bool new_numeric = new_value->get_vbt_value(var_name, &newv); - if (!new_value || !new_value->get_vbt_value(var_name, &newv)) { - return false; + if (!old_numeric || !new_numeric) { + std::string old_text; + std::string new_text; + if (!old_value->get_vbt_text_value(var_name, &old_text) || + !new_value->get_vbt_text_value(var_name, &new_text)) { + return false; + } + + return is_condition_met(old_text, new_text, milliseconds_left); } return is_condition_met(oldv, newv, milliseconds_left); @@ -330,5 +451,7 @@ bool supla_vbt_on_change_condition::is_condition_met( bool supla_vbt_on_change_condition::operator==( const supla_vbt_on_change_condition &cnd) const { return value == cnd.value && var_name == cnd.var_name && op == cnd.op && - resume_op == cnd.resume_op && resume_value == cnd.resume_value; + resume_op == cnd.resume_op && resume_value == cnd.resume_value && + text_comparison == cnd.text_comparison && text_value == cnd.text_value && + duration_sec == cnd.duration_sec && on_change == cnd.on_change; } diff --git a/supla-server/src/vbt/vbt_on_change_condition.h b/supla-server/src/vbt/vbt_on_change_condition.h index 468cbe688b..d22dcf5b3d 100644 --- a/supla-server/src/vbt/vbt_on_change_condition.h +++ b/supla-server/src/vbt/vbt_on_change_condition.h @@ -30,22 +30,33 @@ enum _vbt_operator_e { op_unknown, op_eq, op_ne, op_le, op_lt, op_gt, op_ge }; class supla_vbt_on_change_condition { private: double value; + std::string text_value; _vbt_var_name_e var_name; _vbt_operator_e op; _vbt_operator_e resume_op; int duration_sec; double saved_old_value; + std::string saved_old_text; struct timeval condition_met_at; double resume_value; bool paused; bool on_change; + bool text_comparison; - bool get_operator_and_value(cJSON *root, _vbt_operator_e *op, double *value); + bool get_operator_and_value(cJSON *root, _vbt_operator_e *op, double *value, + std::string *text_value, + bool *text_comparison); bool is_condition_met(_vbt_operator_e op, double old_value, double new_value, double expected); + bool is_condition_met(_vbt_operator_e op, const std::string &old_value, + const std::string &new_value, + const std::string &expected); bool is_condition_met(double old_value, double new_value, _supla_int64_t *milliseconds_left); + bool is_condition_met(const std::string &old_value, + const std::string &new_value, + _supla_int64_t *milliseconds_left); _supla_int64_t ms_left(const struct timeval &now); public: diff --git a/supla-server/src/vbt/vbt_value.cpp b/supla-server/src/vbt/vbt_value.cpp index 97296696fe..55fd59d014 100644 --- a/supla-server/src/vbt/vbt_value.cpp +++ b/supla-server/src/vbt/vbt_value.cpp @@ -31,6 +31,11 @@ bool supla_vbt_value::get_vbt_value(_vbt_var_name_e var_name, double *value) { return false; } +bool supla_vbt_value::get_vbt_text_value(_vbt_var_name_e var_name, + std::string *value) { + return false; +} + nlohmann::json supla_vbt_value::get_template_data(void) { nlohmann::json result; return result; diff --git a/supla-server/src/vbt/vbt_value.h b/supla-server/src/vbt/vbt_value.h index 074ea2c8e4..e03cedd53b 100644 --- a/supla-server/src/vbt/vbt_value.h +++ b/supla-server/src/vbt/vbt_value.h @@ -92,6 +92,8 @@ class supla_vbt_value { supla_vbt_value(void); virtual ~supla_vbt_value(void); virtual bool get_vbt_value(_vbt_var_name_e var_name, double *value); + virtual bool get_vbt_text_value(_vbt_var_name_e var_name, + std::string *value); virtual nlohmann::json get_template_data(void); };