From 81b121b121315011f715e4a048ee8e2c6a3ef130 Mon Sep 17 00:00:00 2001 From: klew Date: Wed, 27 Jan 2021 12:03:38 +0100 Subject: [PATCH 01/38] Tests for rgbw --- extras/test/CMakeLists.txt | 1 + .../test/RgbwDimmerTests/rgbw_base_tests.cpp | 384 ++++++++++++++++++ extras/test/doubles/Arduino.h | 3 + src/CMakeLists.txt | 6 + src/supla/control/rgbw_base.cpp | 2 +- src/supla/control/rgbw_base.h | 4 +- src/supla/storage/storage.cpp | 1 + 7 files changed, 398 insertions(+), 3 deletions(-) create mode 100644 extras/test/RgbwDimmerTests/rgbw_base_tests.cpp diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index 107bf4d8..a5f3543c 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -43,6 +43,7 @@ file(GLOB TEST_SRC InternalPinOutputTests/*.cpp PinStatusLedTests/*.cpp ConditionTests/*.cpp + RgbwDimmerTests/*.cpp ) file(GLOB DOUBLE_SRC doubles/*.cpp) diff --git a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp new file mode 100644 index 00000000..6a325af8 --- /dev/null +++ b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp @@ -0,0 +1,384 @@ +/* + 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 +#include +#include +#include + +using ::testing::Return; + +class RgbwBaseForTest : public Supla::Control::RGBWBase { + public: + MOCK_METHOD(void, setRGBWValueOnDevice, (uint8_t, uint8_t, uint8_t, uint8_t, uint8_t), (override)); +}; + +TEST(RgbwDimmerTests, InitializationWithDefaultValues) { + TimeInterfaceMock time; + + EXPECT_CALL(time, millis) + .WillOnce(Return(1)) + .WillOnce(Return(500)); + + RgbwBaseForTest rgb; + + ASSERT_NE(rgb.getChannel(), nullptr); + + auto ch = rgb.getChannel(); + + EXPECT_EQ(ch->getChannelType(), SUPLA_CHANNELTYPE_DIMMERANDRGBLED); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.onInit(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + +} + + +TEST(RgbwDimmerTests, FrequentSetCommandShouldNotChangeChannelValue) { + TimeInterfaceMock time; + + EXPECT_CALL(time, millis) + .WillOnce(Return(100)) // #1 setRgb (oninit) + .WillOnce(Return(200)) // #1 iterateAlways + .WillOnce(Return(200)) // #2 manual setRgb + .WillOnce(Return(300)) // #2 iterateAlways + .WillOnce(Return(300)) // #3 manual setRgb + .WillOnce(Return(400)) // #3 iterateAlways + .WillOnce(Return(400)) // #4 manual setRgb + .WillOnce(Return(500)) // #4 iterateAlways + .WillOnce(Return(500)) // #5 manual setRgb - should change channel value + .WillOnce(Return(1000)) // #5 iterateAlways +// .WillOnce(Return(1100)) // #6 manual setRgb with toggle - it don't call millis + .WillOnce(Return(1200)) // #6 iterateAlways + ; + RgbwBaseForTest rgb; + + ASSERT_NE(rgb.getChannel(), nullptr); + + auto ch = rgb.getChannel(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + // #1 + rgb.onInit(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + // #2 + rgb.setRGBW(1, 2, 3, 4, 5, false); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + // #3 + rgb.setRGBW(11, 12, 13, 14, 15, false); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + // #4 + rgb.setRGBW(21, 22, 23, 24, 25, false); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + // #5 + rgb.setRGBW(31, 32, 33, 34, 35, false); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 31); + EXPECT_EQ(ch->getValueGreen(), 32); + EXPECT_EQ(ch->getValueBlue(), 33); + EXPECT_EQ(ch->getValueColorBrightness(), 34); + EXPECT_EQ(ch->getValueBrightness(), 35); + + // #6 - with toggle + rgb.setRGBW(41, 42, 43, 44, 45, true); + + EXPECT_EQ(ch->getValueRed(), 31); + EXPECT_EQ(ch->getValueGreen(), 32); + EXPECT_EQ(ch->getValueBlue(), 33); + EXPECT_EQ(ch->getValueColorBrightness(), 34); + EXPECT_EQ(ch->getValueBrightness(), 35); + + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 41); + EXPECT_EQ(ch->getValueGreen(), 42); + EXPECT_EQ(ch->getValueBlue(), 43); + EXPECT_EQ(ch->getValueColorBrightness(), 44); + EXPECT_EQ(ch->getValueBrightness(), 45); +} + +TEST(RgbwDimmerTests, SetValueFromServer) { + TimeInterfaceMock time; + + EXPECT_CALL(time, millis) + .WillOnce(Return(1)) + .WillOnce(Return(500)) + .WillOnce(Return(500)) + .WillOnce(Return(1000)) + .WillOnce(Return(2000)) + .WillOnce(Return(3000)) + .WillOnce(Return(4000)) + .WillOnce(Return(5000)) + ; + + RgbwBaseForTest rgb; + + auto ch = rgb.getChannel(); + + rgb.onInit(); + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + TSD_SuplaChannelNewValue msg = {}; + msg.value[5] = 0; // turn on/off + msg.value[4] = 1; // red + msg.value[3] = 2; // green + msg.value[2] = 3; // blue + msg.value[1] = 4; // colorBrightness + msg.value[0] = 5; // brightness + rgb.handleNewValueFromServer(&msg); + + // channel values should be changed only after some time passed + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + // a little bit later, values are set to channel + EXPECT_EQ(ch->getValueRed(), 1); + EXPECT_EQ(ch->getValueGreen(), 2); + EXPECT_EQ(ch->getValueBlue(), 3); + EXPECT_EQ(ch->getValueColorBrightness(), 4); + EXPECT_EQ(ch->getValueBrightness(), 5); + +// with toggle - should turn off dimmer + msg.value[5] = 1; + msg.value[0] = 0; + rgb.handleNewValueFromServer(&msg); + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 1); + EXPECT_EQ(ch->getValueGreen(), 2); + EXPECT_EQ(ch->getValueBlue(), 3); + EXPECT_EQ(ch->getValueColorBrightness(), 4); + EXPECT_EQ(ch->getValueBrightness(), 0); + +// with toggle - should turn on dimmer and restore last brightness + msg.value[5] = 1; + msg.value[0] = 100; + rgb.handleNewValueFromServer(&msg); + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 1); + EXPECT_EQ(ch->getValueGreen(), 2); + EXPECT_EQ(ch->getValueBlue(), 3); + EXPECT_EQ(ch->getValueColorBrightness(), 4); + EXPECT_EQ(ch->getValueBrightness(), 5); + +// with toggle - should turn off rgb + msg.value[5] = 1; + msg.value[1] = 0; + msg.value[0] = 5; // restore brightness so it is consistant with last value + rgb.handleNewValueFromServer(&msg); + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 1); + EXPECT_EQ(ch->getValueGreen(), 2); + EXPECT_EQ(ch->getValueBlue(), 3); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 5); + +// with toggle - should turn on dimmer and restore last brightness + msg.value[5] = 1; + msg.value[1] = 100; + rgb.handleNewValueFromServer(&msg); + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 1); + EXPECT_EQ(ch->getValueGreen(), 2); + EXPECT_EQ(ch->getValueBlue(), 3); + EXPECT_EQ(ch->getValueColorBrightness(), 4); + EXPECT_EQ(ch->getValueBrightness(), 5); + +} + +TEST(RgbwDimmerTests, TurnOnOffToggleTests) { + TimeInterfaceMock time; + + EXPECT_CALL(time, millis) + .WillOnce(Return(1)) + .WillOnce(Return(500)) + .WillOnce(Return(1000)) + .WillOnce(Return(2000)) + .WillOnce(Return(3000)) + .WillOnce(Return(4000)) + .WillOnce(Return(5000)) + .WillOnce(Return(6000)) + .WillOnce(Return(7000)) + .WillOnce(Return(8000)) + .WillOnce(Return(9000)) + .WillOnce(Return(10000)) + ; + + RgbwBaseForTest rgb; + + auto ch = rgb.getChannel(); + + rgb.onInit(); + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.turnOn(); + // channel values should be changed only after some time passed + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + // a little bit later, values are set to channel + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); + + rgb.turnOff(); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.setRGBW(0, 255, 0, 50, 40, false); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 50); + EXPECT_EQ(ch->getValueBrightness(), 40); + + rgb.toggle(); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.toggle(); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 50); + EXPECT_EQ(ch->getValueBrightness(), 40); + + +} diff --git a/extras/test/doubles/Arduino.h b/extras/test/doubles/Arduino.h index 518485d2..aa127418 100644 --- a/extras/test/doubles/Arduino.h +++ b/extras/test/doubles/Arduino.h @@ -14,6 +14,9 @@ typedef std::string String; #define HIGH 1 #define LOW 0 + +#define F(string_literal) string_literal + void digitalWrite(uint8_t pin, uint8_t val); int digitalRead(uint8_t pin); void pinMode(uint8_t pin, uint8_t mode); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 24760701..5a123b8a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,8 +7,13 @@ set(SRCS supla/element.cpp supla/local_action.cpp supla/channel_element.cpp + + supla/storage/storage.cpp + supla/control/internal_pin_output.cpp supla/control/pin_status_led.cpp + supla/control/rgbw_base.cpp + supla/condition.cpp supla/conditions/on_less.cpp supla/conditions/on_less_eq.cpp @@ -17,6 +22,7 @@ set(SRCS supla/conditions/on_between.cpp supla/conditions/on_between_eq.cpp supla/conditions/on_equal.cpp + ) add_library(supladevicelib SHARED ${SRCS}) diff --git a/src/supla/control/rgbw_base.cpp b/src/supla/control/rgbw_base.cpp index 4f2845ff..790cde9f 100644 --- a/src/supla/control/rgbw_base.cpp +++ b/src/supla/control/rgbw_base.cpp @@ -47,7 +47,7 @@ RGBWBase::RGBWBase() hwBrightness(0), lastTick(0), lastMsgReceivedMs(0), - stateOnInit(RGBW_STATE_ON_INIT_OFF) { + stateOnInit(RGBW_STATE_ON_INIT_RESTORE) { channel.setType(SUPLA_CHANNELTYPE_DIMMERANDRGBLED); channel.setDefault(SUPLA_CHANNELFNC_DIMMERANDRGBLIGHTING); } diff --git a/src/supla/control/rgbw_base.h b/src/supla/control/rgbw_base.h index 5ab19fd7..e9287f41 100644 --- a/src/supla/control/rgbw_base.h +++ b/src/supla/control/rgbw_base.h @@ -51,10 +51,10 @@ class RGBWBase : public ChannelElement, public ActionHandler { void setStep(int step); void setDefaultDimmedBrightness(int dimmedBrightness); void setFadeEffectTime(int timeMs); - void onTimer(); - void iterateAlways(); void onInit(); + void iterateAlways(); + void onTimer(); void onLoadState(); void onSaveState(); diff --git a/src/supla/storage/storage.cpp b/src/supla/storage/storage.cpp index 8e21d098..c9f9d974 100644 --- a/src/supla/storage/storage.cpp +++ b/src/supla/storage/storage.cpp @@ -15,6 +15,7 @@ */ #include +#include #include "storage.h" From 98a9938d22406971b44dd44e0d2894d3baa87db8 Mon Sep 17 00:00:00 2001 From: klew Date: Wed, 27 Jan 2021 14:02:26 +0100 Subject: [PATCH 02/38] RGBW tests --- .../test/RgbwDimmerTests/rgbw_base_tests.cpp | 673 ++++++++++++++++++ 1 file changed, 673 insertions(+) diff --git a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp index 6a325af8..863fa799 100644 --- a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp +++ b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp @@ -18,6 +18,7 @@ #include #include #include +#include using ::testing::Return; @@ -26,6 +27,15 @@ class RgbwBaseForTest : public Supla::Control::RGBWBase { MOCK_METHOD(void, setRGBWValueOnDevice, (uint8_t, uint8_t, uint8_t, uint8_t, uint8_t), (override)); }; +class TimeInterfaceStub : public TimeInterface { + public: + virtual unsigned long millis() override { + static unsigned long value = 0; + value += 1000; + return value; + } +}; + TEST(RgbwDimmerTests, InitializationWithDefaultValues) { TimeInterfaceMock time; @@ -379,6 +389,669 @@ TEST(RgbwDimmerTests, TurnOnOffToggleTests) { EXPECT_EQ(ch->getValueBlue(), 0); EXPECT_EQ(ch->getValueColorBrightness(), 50); EXPECT_EQ(ch->getValueBrightness(), 40); +} + +TEST(RgbwDimmerTests, HandleActionTests) { + // time stub will return +1000 ms on each call to millis + TimeInterfaceStub time; + + RgbwBaseForTest rgb; + + auto ch = rgb.getChannel(); + + rgb.onInit(); + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::TURN_ON); + // channel values should be changed only after some time passed + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + // a little bit later, values are set to channel + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); + + rgb.handleAction(1, Supla::TURN_OFF); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::BRIGHTEN_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 10); + EXPECT_EQ(ch->getValueBrightness(), 10); + + rgb.handleAction(1, Supla::BRIGHTEN_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 20); + + rgb.handleAction(1, Supla::TOGGLE); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::TOGGLE); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 20); + + rgb.handleAction(1, Supla::DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 10); + EXPECT_EQ(ch->getValueBrightness(), 10); + + rgb.handleAction(1, Supla::DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::TURN_ON); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 10); + EXPECT_EQ(ch->getValueBrightness(), 10); + + for (int i = 0; i < 20; i++) { + rgb.handleAction(1, Supla::BRIGHTEN_ALL); + }; + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); + + rgb.handleAction(1, Supla::BRIGHTEN_R); + rgb.handleAction(1, Supla::BRIGHTEN_R); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 20); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); + + rgb.handleAction(1, Supla::DIM_R); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); + + rgb.handleAction(1, Supla::DIM_G); + rgb.handleAction(1, Supla::DIM_G); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 235); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); + + rgb.handleAction(1, Supla::BRIGHTEN_G); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); + + + rgb.handleAction(1, Supla::BRIGHTEN_B); + rgb.handleAction(1, Supla::BRIGHTEN_B); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 20); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); + + rgb.handleAction(1, Supla::DIM_B); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); + + rgb.handleAction(1, Supla::DIM_W); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 90); + + rgb.handleAction(1, Supla::DIM_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 90); + + rgb.handleAction(1, Supla::BRIGHTEN_W); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 100); + + rgb.handleAction(1, Supla::BRIGHTEN_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); + + rgb.handleAction(1, Supla::DIM_ALL); + rgb.handleAction(1, Supla::TURN_OFF_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 90); + + rgb.handleAction(1, Supla::TURN_ON_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 90); + + rgb.handleAction(1, Supla::TURN_OFF_W); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::TURN_ON_W); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 90); + + rgb.handleAction(1, Supla::TOGGLE_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 90); + + rgb.handleAction(1, Supla::TOGGLE_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 90); + rgb.handleAction(1, Supla::TOGGLE_W); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::TOGGLE_W); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 90); + + rgb.handleAction(1, Supla::TURN_ON_RGB_DIMMED); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 90); + + rgb.handleAction(1, Supla::TURN_ON_W_DIMMED); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 90); + + rgb.handleAction(1, Supla::TURN_ON_ALL_DIMMED); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 90); + EXPECT_EQ(ch->getValueBrightness(), 90); + + rgb.handleAction(1, Supla::TURN_OFF); + rgb.handleAction(1, Supla::TURN_ON_RGB_DIMMED); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::TURN_ON_W_DIMMED); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 20); + + rgb.handleAction(1, Supla::TURN_OFF); + rgb.handleAction(1, Supla::TURN_ON_ALL_DIMMED); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 20); + + rgb.handleAction(1, Supla::ITERATE_DIM_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 30); + EXPECT_EQ(ch->getValueBrightness(), 20); + + rgb.handleAction(1, Supla::ITERATE_DIM_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 40); + EXPECT_EQ(ch->getValueBrightness(), 20); + + for (int i = 0; i < 6; i++) { + rgb.handleAction(1, Supla::ITERATE_DIM_RGB); + } + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 20); + + // after hitting 100%, iterate should stay 4 iterations before it goes down + for (int i = 0; i < 4; i++) { + rgb.handleAction(1, Supla::ITERATE_DIM_RGB); + } + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 20); + + // next five iterations should change brightness to 50 + for (int i = 0; i < 5; i++) { + rgb.handleAction(1, Supla::ITERATE_DIM_RGB); + } + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 50); + EXPECT_EQ(ch->getValueBrightness(), 20); + + // next five iterations should change brightness to 0 + for (int i = 0; i < 4; i++) { + rgb.handleAction(1, Supla::ITERATE_DIM_RGB); + } + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 10); + EXPECT_EQ(ch->getValueBrightness(), 20); + + // next four iterations should keep 0 + for (int i = 0; i < 4; i++) { + rgb.handleAction(1, Supla::ITERATE_DIM_RGB); + } + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 10); + EXPECT_EQ(ch->getValueBrightness(), 20); + + rgb.handleAction(1, Supla::ITERATE_DIM_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 20); + + // turn off + rgb.handleAction(1, Supla::TURN_OFF); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + // after turn off, it should start from dimmed value + rgb.handleAction(1, Supla::ITERATE_DIM_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 10); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::ITERATE_DIM_RGB); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::ITERATE_DIM_W); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 10); + + rgb.handleAction(1, Supla::ITERATE_DIM_W); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 20); + + for (int i = 0; i < 12; i++) { + rgb.handleAction(1, Supla::ITERATE_DIM_W); + } + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 100); + + // if we iterate all, colorBrightness is copied to brightness and it operated on both values in sync + rgb.handleAction(1, Supla::ITERATE_DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 30); + EXPECT_EQ(ch->getValueBrightness(), 30); + + rgb.handleAction(1, Supla::ITERATE_DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 10); + EXPECT_EQ(ch->getValueGreen(), 245); + EXPECT_EQ(ch->getValueBlue(), 10); + EXPECT_EQ(ch->getValueColorBrightness(), 40); + EXPECT_EQ(ch->getValueBrightness(), 40); +} + + +TEST(RgbwDimmerTests, DefaultDimmedValue) { + // time stub will return +1000 ms on each call to millis + TimeInterfaceStub time; + + RgbwBaseForTest rgb; + + auto ch = rgb.getChannel(); + + rgb.onInit(); + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::TURN_ON_ALL_DIMMED); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 20); + + rgb.handleAction(1, Supla::TURN_OFF); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.setDefaultDimmedBrightness(64); + rgb.handleAction(1, Supla::TURN_ON_ALL_DIMMED); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 64); + EXPECT_EQ(ch->getValueBrightness(), 64); +} + +TEST(RgbwDimmerTests, IterationSteps) { + // time stub will return +1000 ms on each call to millis + TimeInterfaceStub time; + + RgbwBaseForTest rgb; + + auto ch = rgb.getChannel(); + + rgb.onInit(); + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::ITERATE_DIM_ALL); + rgb.handleAction(1, Supla::ITERATE_DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 20); + + rgb.setStep(5); + rgb.handleAction(1, Supla::ITERATE_DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 25); + EXPECT_EQ(ch->getValueBrightness(), 25); +} + +TEST(RgbwDimmerTests, SetValueOnDeviceWithoutFading) { + // time stub will return +1000 ms on each call to millis + TimeInterfaceStub time; + ::testing::InSequence seq; + + RgbwBaseForTest rgb; + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 100, 100)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 100, 20)); + + + auto ch = rgb.getChannel(); + + // disable fade effect - so value setting on device should happen instantly + rgb.setFadeEffectTime(0); + rgb.onInit(); + rgb.turnOn(); + rgb.toggle(); + rgb.handleAction(1, Supla::TURN_ON_W_DIMMED); + rgb.turnOff(); + rgb.turnOn(); + + // channel value should be still empty, since no time elapsed (no calls to iterateAlways) + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); +} + +TEST(RgbwDimmerTests, SetValueOnDeviceWithFading) { + // time stub will return +1000 ms on each call to millis + TimeInterfaceStub time; + ::testing::InSequence seq; + + RgbwBaseForTest rgb; + + // fade effect 1000 ms, time step 1000 ms + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 100, 100)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 100, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); + + // fade effect 10000 ms, time step 1000 ms + // because turnOn calls millis once, we actually do 2 s step in first shot + // that is why (0, 255, 0, 10, 10) is missing + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 20, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 30, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 40, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 50, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 60, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 70, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 80, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 90, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 100, 20)); + + // fade effect 10000 ms, time step 1000 ms + // because turnOn calls millis once, we actually do 2 s step in first shot + // setting rgb values + EXPECT_CALL(rgb, setRGBWValueOnDevice(51, 204, 0, 100, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(76, 200, 0, 100, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(100, 200, 0, 100, 20)); + + auto ch = rgb.getChannel(); + + // time stub gives +1000 ms on each call to millis, and fade time is 1000 ms, + // so it should set value on device as it is + rgb.onInit(); + rgb.onTimer(); + rgb.turnOn(); + rgb.onTimer(); + rgb.toggle(); + rgb.onTimer(); + rgb.handleAction(1, Supla::TURN_ON_W_DIMMED); + rgb.onTimer(); + rgb.turnOff(); + rgb.onTimer(); + rgb.turnOn(); + rgb.onTimer(); + rgb.turnOff(); + rgb.onTimer(); + + // change fade effect to 10000 ms, so we'll get 1/10 steps + rgb.setFadeEffectTime(10000); + rgb.turnOn(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + + rgb.setRGBW(100, 200, 0, -1, -1, false); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + rgb.onTimer(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); } From 5625f1289f9c2c780f2060e49b4c373e3d8f4feb Mon Sep 17 00:00:00 2001 From: klew Date: Thu, 28 Jan 2021 00:20:32 +0100 Subject: [PATCH 03/38] Tests for RGBW storage --- .../test/RgbwDimmerTests/rgbw_base_tests.cpp | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp index 863fa799..d55544d8 100644 --- a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp +++ b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp @@ -19,6 +19,7 @@ #include #include #include +#include using ::testing::Return; @@ -1053,5 +1054,66 @@ TEST(RgbwDimmerTests, SetValueOnDeviceWithFading) { EXPECT_EQ(ch->getValueBlue(), 0); EXPECT_EQ(ch->getValueColorBrightness(), 0); EXPECT_EQ(ch->getValueBrightness(), 0); +} + +class StorageMock: public Supla::Storage { + public: + MOCK_METHOD(void, scheduleSave, (unsigned long), (override)); + MOCK_METHOD(void, commit, (), (override)); + MOCK_METHOD(int, readStorage, (unsigned int, unsigned char *, int, bool), (override)); + MOCK_METHOD(int, writeStorage, (unsigned int, const unsigned char *, int), (override)); + MOCK_METHOD(bool, readState, (unsigned char *, int), (override)); + MOCK_METHOD(bool, writeState, (const unsigned char *, int), (override)); + +}; + +using ::testing::_; +using ::testing::SetArgPointee; +using ::testing::DoAll; +using ::testing::Pointee; + +TEST(RgbwDimmerTests, RgbwStorageTests) { + // time stub will return +1000 ms on each call to millis + TimeInterfaceStub time; + ::testing::InSequence seq; + + StorageMock storage; + RgbwBaseForTest rgb; + + // setRGBW should call scheduleSave on storage once + EXPECT_CALL(storage, scheduleSave(5000)); + + uint8_t red = 1; + uint8_t green = 2; + uint8_t blue = 3; + uint8_t colorBrightness = 4; + uint8_t brightness = 5; + uint8_t lastColorBrightness = 6; + uint8_t lastBrightness = 7; + + // onLoadState expectations + EXPECT_CALL(storage, readState(_, 1)) + .WillOnce(DoAll(SetArgPointee<0>(red), Return(true))) + .WillOnce(DoAll(SetArgPointee<0>(green), Return(true))) + .WillOnce(DoAll(SetArgPointee<0>(blue), Return(true))) + .WillOnce(DoAll(SetArgPointee<0>(colorBrightness), Return(true))) + .WillOnce(DoAll(SetArgPointee<0>(brightness), Return(true))) + .WillOnce(DoAll(SetArgPointee<0>(lastColorBrightness), Return(true))) + .WillOnce(DoAll(SetArgPointee<0>(lastBrightness), Return(true))) + ; + + // onSaveState expectations + EXPECT_CALL(storage, writeState(Pointee(red), 1)); + EXPECT_CALL(storage, writeState(Pointee(green), 1)); + EXPECT_CALL(storage, writeState(Pointee(blue), 1)); + EXPECT_CALL(storage, writeState(Pointee(colorBrightness), 1)); + EXPECT_CALL(storage, writeState(Pointee(brightness), 1)); + EXPECT_CALL(storage, writeState(Pointee(lastColorBrightness), 1)); + EXPECT_CALL(storage, writeState(Pointee(lastBrightness), 1)); + + rgb.setRGBW(1, 2, 3, 4, 5, false); + + rgb.onLoadState(); + rgb.onSaveState(); } From a4e8d0b9259fadc0719092a02e43246378c3fbf0 Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 29 Jan 2021 00:07:04 +0100 Subject: [PATCH 04/38] Added tests for dimmer and rgb base classes --- extras/test/RgbwDimmerTests/dimmer_tests.cpp | 120 ++++++++++++++++++ .../test/RgbwDimmerTests/rgb_base_tests.cpp | 119 +++++++++++++++++ .../test/RgbwDimmerTests/rgbw_base_tests.cpp | 1 + src/CMakeLists.txt | 2 + src/supla/control/dimmer_base.h | 2 +- src/supla/control/rgb_base.cpp | 61 +++++++++ src/supla/control/rgb_base.h | 12 +- src/supla/control/rgbw_base.cpp | 2 +- src/supla/control/rgbw_base.h | 2 +- 9 files changed, 310 insertions(+), 11 deletions(-) create mode 100644 extras/test/RgbwDimmerTests/dimmer_tests.cpp create mode 100644 extras/test/RgbwDimmerTests/rgb_base_tests.cpp create mode 100644 src/supla/control/rgb_base.cpp diff --git a/extras/test/RgbwDimmerTests/dimmer_tests.cpp b/extras/test/RgbwDimmerTests/dimmer_tests.cpp new file mode 100644 index 00000000..c9b90c8f --- /dev/null +++ b/extras/test/RgbwDimmerTests/dimmer_tests.cpp @@ -0,0 +1,120 @@ +/* + 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 +#include +#include +#include +#include +#include + +using ::testing::Return; + +class DimmerBaseForTest : public Supla::Control::DimmerBase { + public: + MOCK_METHOD(void, setRGBWValueOnDevice, (uint8_t, uint8_t, uint8_t, uint8_t, uint8_t), (override)); +}; + +class TimeInterfaceStub : public TimeInterface { + public: + virtual unsigned long millis() override { + static unsigned long value = 0; + value += 1000; + return value; + } +}; + +TEST(DimmerTests, InitializationWithDefaultValues) { + TimeInterfaceStub time; + + DimmerBaseForTest dimmer; + + ASSERT_NE(dimmer.getChannel(), nullptr); + + auto ch = dimmer.getChannel(); + + EXPECT_EQ(ch->getChannelType(), SUPLA_CHANNELTYPE_DIMMER); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + dimmer.onInit(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + dimmer.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); +} + +TEST(DimmerTests, DimmerShouldIgnoreRGBValues) { + TimeInterfaceStub time; + + DimmerBaseForTest dimmer; + + auto ch = dimmer.getChannel(); + // disable fading effect so we'll get instant setting value on device call + dimmer.setFadeEffectTime(0); + + EXPECT_CALL(dimmer, setRGBWValueOnDevice(0, 0, 0, 0, 0)).Times(1); + EXPECT_CALL(dimmer, setRGBWValueOnDevice(0, 0, 0, 0, 5)).Times(1); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + dimmer.onInit(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + dimmer.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + // we call with rgbw settings, which should be passed as 0 + dimmer.setRGBW(1, 2, 3, 4, 5); + + dimmer.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 5); + +} + + diff --git a/extras/test/RgbwDimmerTests/rgb_base_tests.cpp b/extras/test/RgbwDimmerTests/rgb_base_tests.cpp new file mode 100644 index 00000000..b641a82c --- /dev/null +++ b/extras/test/RgbwDimmerTests/rgb_base_tests.cpp @@ -0,0 +1,119 @@ +/* + 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 +#include +#include +#include +#include +#include + +using ::testing::Return; + +class RgbBaseForTest : public Supla::Control::RGBBase { + public: + MOCK_METHOD(void, setRGBWValueOnDevice, (uint8_t, uint8_t, uint8_t, uint8_t, uint8_t), (override)); +}; + +class TimeInterfaceStub : public TimeInterface { + public: + virtual unsigned long millis() override { + static unsigned long value = 0; + value += 1000; + return value; + } +}; + +TEST(RgbTests, InitializationWithDefaultValues) { + TimeInterfaceStub time; + + RgbBaseForTest rgb; + + ASSERT_NE(rgb.getChannel(), nullptr); + + auto ch = rgb.getChannel(); + + EXPECT_EQ(ch->getChannelType(), SUPLA_CHANNELTYPE_RGBLEDCONTROLLER); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.onInit(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); +} + +TEST(RgbTests, RgbShouldIgnoreBrightnessValue) { + TimeInterfaceStub time; + + RgbBaseForTest rgb; + + auto ch = rgb.getChannel(); + // disable fading effect so we'll get instant setting value on device call + rgb.setFadeEffectTime(0); + + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)).Times(1); + EXPECT_CALL(rgb, setRGBWValueOnDevice(1, 2, 3, 4, 0)).Times(1); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.onInit(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + // we call with 5 as brightness - it should be passed as 0 to device and to channel + rgb.setRGBW(1, 2, 3, 4, 5); + + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 1); + EXPECT_EQ(ch->getValueGreen(), 2); + EXPECT_EQ(ch->getValueBlue(), 3); + EXPECT_EQ(ch->getValueColorBrightness(), 4); + EXPECT_EQ(ch->getValueBrightness(), 0); + +} + diff --git a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp index d55544d8..5bbfddc6 100644 --- a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp +++ b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp @@ -1117,3 +1117,4 @@ TEST(RgbwDimmerTests, RgbwStorageTests) { rgb.onSaveState(); } + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a123b8a..e579e930 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,6 +13,8 @@ set(SRCS supla/control/internal_pin_output.cpp supla/control/pin_status_led.cpp supla/control/rgbw_base.cpp + supla/control/rgb_base.cpp + supla/control/dimmer_base.cpp supla/condition.cpp supla/conditions/on_less.cpp diff --git a/src/supla/control/dimmer_base.h b/src/supla/control/dimmer_base.h index ebbe06a5..2d38da1d 100644 --- a/src/supla/control/dimmer_base.h +++ b/src/supla/control/dimmer_base.h @@ -31,7 +31,7 @@ class DimmerBase : public RGBWBase { int blue, int colorBrightness, int brightness, - bool toggle); + bool toggle = false); void onLoadState(); void onSaveState(); diff --git a/src/supla/control/rgb_base.cpp b/src/supla/control/rgb_base.cpp new file mode 100644 index 00000000..9869e5a1 --- /dev/null +++ b/src/supla/control/rgb_base.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 "rgb_base.h" +#include "../storage/storage.h" + +Supla::Control::RGBBase::RGBBase() { + channel.setType(SUPLA_CHANNELTYPE_RGBLEDCONTROLLER); + channel.setDefault(SUPLA_CHANNELFNC_RGBLIGHTING); +} + +void Supla::Control::RGBBase::setRGBW(int red, + int green, + int blue, + int colorBrightness, + int brightness, + bool toggle) { + Supla::Control::RGBWBase::setRGBW( + red, green, blue, colorBrightness, 0, toggle); +} + +void Supla::Control::RGBBase::onSaveState() { + /* + uint8_t curRed; // 0 - 255 + uint8_t curGreen; // 0 - 255 + uint8_t curBlue; // 0 - 255 + uint8_t curColorBrightness; // 0 - 100 + uint8_t lastColorBrightness; // 0 - 100 + */ + Supla::Storage::WriteState((unsigned char *)&curRed, sizeof(curRed)); + Supla::Storage::WriteState((unsigned char *)&curGreen, sizeof(curGreen)); + Supla::Storage::WriteState((unsigned char *)&curBlue, sizeof(curBlue)); + Supla::Storage::WriteState((unsigned char *)&curColorBrightness, + sizeof(curColorBrightness)); + Supla::Storage::WriteState((unsigned char *)&lastColorBrightness, + sizeof(lastColorBrightness)); +} + +void Supla::Control::RGBBase::onLoadState() { + Supla::Storage::ReadState((unsigned char *)&curRed, sizeof(curRed)); + Supla::Storage::ReadState((unsigned char *)&curGreen, sizeof(curGreen)); + Supla::Storage::ReadState((unsigned char *)&curBlue, sizeof(curBlue)); + Supla::Storage::ReadState((unsigned char *)&curColorBrightness, + sizeof(curColorBrightness)); + Supla::Storage::ReadState((unsigned char *)&lastColorBrightness, + sizeof(lastColorBrightness)); +} + diff --git a/src/supla/control/rgb_base.h b/src/supla/control/rgb_base.h index 75afb29e..2bc534d9 100644 --- a/src/supla/control/rgb_base.h +++ b/src/supla/control/rgb_base.h @@ -23,20 +23,16 @@ namespace Supla { namespace Control { class RGBBase : public RGBWBase { public: - RGBBase() { - channel.setType(SUPLA_CHANNELTYPE_RGBLEDCONTROLLER); - channel.setDefault(SUPLA_CHANNELFNC_RGBLIGHTING); - } - + RGBBase(); void setRGBW(int red, int green, int blue, int colorBrightness, int brightness, - bool toggle) { - RGBWBase::setRGBW(red, green, blue, colorBrightness, 0, toggle); - } + bool toggle = false); + void onLoadState(); + void onSaveState(); }; }; // namespace Control diff --git a/src/supla/control/rgbw_base.cpp b/src/supla/control/rgbw_base.cpp index 790cde9f..03a6ca05 100644 --- a/src/supla/control/rgbw_base.cpp +++ b/src/supla/control/rgbw_base.cpp @@ -57,7 +57,7 @@ void RGBWBase::setRGBW(int red, int blue, int colorBrightness, int brightness, - bool toggle = false) { + bool toggle) { if (toggle) { lastMsgReceivedMs = 1; } else { diff --git a/src/supla/control/rgbw_base.h b/src/supla/control/rgbw_base.h index e9287f41..53429dfc 100644 --- a/src/supla/control/rgbw_base.h +++ b/src/supla/control/rgbw_base.h @@ -41,7 +41,7 @@ class RGBWBase : public ChannelElement, public ActionHandler { int blue, int colorBrightness, int brightness, - bool toggle); + bool toggle = false); int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue); virtual void turnOn(); From 5f3fb181c25cd6d4395693675f00dea887e665b3 Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 29 Jan 2021 00:43:05 +0100 Subject: [PATCH 05/38] Added repeated ON_HOLD action for button (configured by repeateOnHoldEvery(timeMs) method) --- extras/test/RgbwDimmerTests/rgbw_base_tests.cpp | 2 ++ src/supla/control/button.cpp | 16 ++++++++++------ src/supla/control/button.h | 4 +++- src/supla/control/rgbw_base.cpp | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp index 5bbfddc6..6cccba01 100644 --- a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp +++ b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp @@ -400,6 +400,7 @@ TEST(RgbwDimmerTests, HandleActionTests) { auto ch = rgb.getChannel(); + rgb.setStep(10); rgb.onInit(); rgb.iterateAlways(); @@ -904,6 +905,7 @@ TEST(RgbwDimmerTests, IterationSteps) { TimeInterfaceStub time; RgbwBaseForTest rgb; + rgb.setStep(10); auto ch = rgb.getChannel(); diff --git a/src/supla/control/button.cpp b/src/supla/control/button.cpp index 8fcb294e..9971de8c 100644 --- a/src/supla/control/button.cpp +++ b/src/supla/control/button.cpp @@ -20,10 +20,11 @@ Supla::Control::Button::Button(int pin, bool pullUp, bool invertLogic) : SimpleButton(pin, pullUp, invertLogic), holdTimeMs(0), + repeatOnHoldMs(0), multiclickTimeMs(0), lastStateChangeMs(0), clickCounter(0), - holdSend(false), + holdSend(0), bistable(false) { } @@ -50,17 +51,17 @@ void Supla::Control::Button::onTimer() { if (!stateChanged) { if (!bistable && stateResult == PRESSED) { - if (clickCounter <= 1 && holdTimeMs > 0 && timeDelta > holdTimeMs && !holdSend) { + if (clickCounter <= 1 && holdTimeMs > 0 && timeDelta > (holdTimeMs + holdSend*repeatOnHoldMs) && (repeatOnHoldMs == 0 ? !holdSend : true)) { runAction(ON_HOLD); - holdSend = true; + ++holdSend; } } else if (clickCounter > 0 && (bistable || stateResult == RELEASED)) { if (multiclickTimeMs == 0) { - holdSend = false; + holdSend = 0; clickCounter = 0; } if (multiclickTimeMs > 0 && timeDelta > multiclickTimeMs) { - if (!holdSend) { + if (holdSend == 0) { switch (clickCounter) { case 1: runAction(ON_CLICK_1); @@ -97,7 +98,7 @@ void Supla::Control::Button::onTimer() { runAction(ON_CRAZY_CLICKER); } } - holdSend = false; + holdSend = 0; clickCounter = 0; } } @@ -119,3 +120,6 @@ void Supla::Control::Button::setMulticlickTime(unsigned int timeMs, bool bistabl } } +void Supla::Control::Button::repeatOnHoldEvery(unsigned int timeMs) { + repeatOnHoldMs = timeMs; +} diff --git a/src/supla/control/button.h b/src/supla/control/button.h index a48328d5..ba31ab58 100644 --- a/src/supla/control/button.h +++ b/src/supla/control/button.h @@ -29,14 +29,16 @@ class Button : public SimpleButton { void onTimer(); void setHoldTime(unsigned int timeMs); + void repeatOnHoldEvery(unsigned int timeMs); void setMulticlickTime(unsigned int timeMs, bool bistableButton = false); protected: unsigned int holdTimeMs; + unsigned int repeatOnHoldMs; unsigned int multiclickTimeMs; unsigned long lastStateChangeMs; uint8_t clickCounter; - bool holdSend; + unsigned int holdSend; bool bistable; }; diff --git a/src/supla/control/rgbw_base.cpp b/src/supla/control/rgbw_base.cpp index 03a6ca05..aa2da0b2 100644 --- a/src/supla/control/rgbw_base.cpp +++ b/src/supla/control/rgbw_base.cpp @@ -28,7 +28,7 @@ namespace Supla { namespace Control { RGBWBase::RGBWBase() - : buttonStep(10), + : buttonStep(5), curRed(0), curGreen(255), curBlue(0), From ac90aa8d95b332d1359c6cfeda34a27972faa660 Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 29 Jan 2021 12:13:24 +0100 Subject: [PATCH 06/38] Added RGBWLeds, RGBLeds, DimmerLeds - to control light via PWM signal --- .../RgbwDimmerTests/dimmer_leds_tests.cpp | 83 +++++++++++++++++ .../test/RgbwDimmerTests/rgb_leds_tests.cpp | 91 ++++++++++++++++++ .../test/RgbwDimmerTests/rgbw_leds_test.cpp | 93 +++++++++++++++++++ extras/test/doubles/Arduino.h | 1 + extras/test/doubles/arduino_mock.cpp | 4 + extras/test/doubles/arduino_mock.h | 2 + src/CMakeLists.txt | 3 + src/supla/control/dimmer_leds.cpp | 39 ++++++++ src/supla/control/dimmer_leds.h | 44 +++++++++ src/supla/control/rgb_leds.cpp | 52 +++++++++++ src/supla/control/rgb_leds.h | 46 +++++++++ src/supla/control/rgbw_leds.cpp | 55 +++++++++++ src/supla/control/rgbw_leds.h | 51 ++++++++++ src/supla/storage/storage.cpp | 4 + src/supla/storage/storage.h | 1 + 15 files changed, 569 insertions(+) create mode 100644 extras/test/RgbwDimmerTests/dimmer_leds_tests.cpp create mode 100644 extras/test/RgbwDimmerTests/rgb_leds_tests.cpp create mode 100644 extras/test/RgbwDimmerTests/rgbw_leds_test.cpp create mode 100644 src/supla/control/dimmer_leds.cpp create mode 100644 src/supla/control/dimmer_leds.h create mode 100644 src/supla/control/rgb_leds.cpp create mode 100644 src/supla/control/rgb_leds.h create mode 100644 src/supla/control/rgbw_leds.cpp create mode 100644 src/supla/control/rgbw_leds.h diff --git a/extras/test/RgbwDimmerTests/dimmer_leds_tests.cpp b/extras/test/RgbwDimmerTests/dimmer_leds_tests.cpp new file mode 100644 index 00000000..f0b41062 --- /dev/null +++ b/extras/test/RgbwDimmerTests/dimmer_leds_tests.cpp @@ -0,0 +1,83 @@ +/* + 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 +#include +#include +#include + +using ::testing::Return; + +class TimeInterfaceStub : public TimeInterface { + public: + virtual unsigned long millis() override { + static unsigned long value = 0; + value += 1000; + return value; + } +}; + +TEST(DimmerLedsTests, SettingNewDimValue) { + TimeInterfaceStub time; + DigitalInterfaceMock ioMock; + + EXPECT_CALL(ioMock, pinMode(1, OUTPUT)); + EXPECT_CALL(ioMock, analogWrite(1, 0)); + + EXPECT_CALL(ioMock, analogWrite(1, (255*10)/100)); + + Supla::Control::DimmerLeds dim(1); + + auto ch = dim.getChannel(); + // disable fading effect so we'll get instant setting value on device call + dim.setFadeEffectTime(0); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + dim.onInit(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + dim.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + dim.setRGBW(1, 2, 3, 4, 10); + + dim.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 10); + +} + + + diff --git a/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp b/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp new file mode 100644 index 00000000..02bd948b --- /dev/null +++ b/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp @@ -0,0 +1,91 @@ +/* + 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 +#include +#include +#include + +using ::testing::Return; + +class TimeInterfaceStub : public TimeInterface { + public: + virtual unsigned long millis() override { + static unsigned long value = 0; + value += 1000; + return value; + } +}; + +TEST(RgbLedsTests, SettingNewRGBValue) { + TimeInterfaceStub time; + DigitalInterfaceMock ioMock; + + EXPECT_CALL(ioMock, pinMode(1, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(2, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(3, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(4, OUTPUT)); + EXPECT_CALL(ioMock, analogWrite(1, 0)); + EXPECT_CALL(ioMock, analogWrite(2, 255)); + EXPECT_CALL(ioMock, analogWrite(3, 0)); + EXPECT_CALL(ioMock, analogWrite(4, 0)); + + EXPECT_CALL(ioMock, analogWrite(1, 1)); + EXPECT_CALL(ioMock, analogWrite(2, 2)); + EXPECT_CALL(ioMock, analogWrite(3, 3)); + EXPECT_CALL(ioMock, analogWrite(4, (255*4)/100)); + + Supla::Control::RGBLeds rgbw(1, 2, 3, 4); + + auto ch = rgbw.getChannel(); + // disable fading effect so we'll get instant setting value on device call + rgbw.setFadeEffectTime(0); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgbw.onInit(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgbw.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgbw.setRGBW(1, 2, 3, 4, 5); + + rgbw.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 1); + EXPECT_EQ(ch->getValueGreen(), 2); + EXPECT_EQ(ch->getValueBlue(), 3); + EXPECT_EQ(ch->getValueColorBrightness(), 4); + EXPECT_EQ(ch->getValueBrightness(), 0); + +} + + diff --git a/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp b/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp new file mode 100644 index 00000000..09aca654 --- /dev/null +++ b/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp @@ -0,0 +1,93 @@ +/* + 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 +#include +#include +#include + +using ::testing::Return; + +class TimeInterfaceStub : public TimeInterface { + public: + virtual unsigned long millis() override { + static unsigned long value = 0; + value += 1000; + return value; + } +}; + +TEST(RgbwLedsTests, SettingNewRGBWValue) { + TimeInterfaceStub time; + DigitalInterfaceMock ioMock; + + EXPECT_CALL(ioMock, pinMode(1, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(2, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(3, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(4, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(5, OUTPUT)); + EXPECT_CALL(ioMock, analogWrite(1, 0)); + EXPECT_CALL(ioMock, analogWrite(2, 255)); + EXPECT_CALL(ioMock, analogWrite(3, 0)); + EXPECT_CALL(ioMock, analogWrite(4, 0)); + EXPECT_CALL(ioMock, analogWrite(5, 0)); + + EXPECT_CALL(ioMock, analogWrite(1, 1)); + EXPECT_CALL(ioMock, analogWrite(2, 2)); + EXPECT_CALL(ioMock, analogWrite(3, 3)); + EXPECT_CALL(ioMock, analogWrite(4, (255*4)/100)); + EXPECT_CALL(ioMock, analogWrite(5, (255*5)/100)); + + Supla::Control::RGBWLeds rgbw(1, 2, 3, 4, 5); + + auto ch = rgbw.getChannel(); + // disable fading effect so we'll get instant setting value on device call + rgbw.setFadeEffectTime(0); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgbw.onInit(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgbw.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgbw.setRGBW(1, 2, 3, 4, 5); + + rgbw.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 1); + EXPECT_EQ(ch->getValueGreen(), 2); + EXPECT_EQ(ch->getValueBlue(), 3); + EXPECT_EQ(ch->getValueColorBrightness(), 4); + EXPECT_EQ(ch->getValueBrightness(), 5); + +} + diff --git a/extras/test/doubles/Arduino.h b/extras/test/doubles/Arduino.h index aa127418..066c9729 100644 --- a/extras/test/doubles/Arduino.h +++ b/extras/test/doubles/Arduino.h @@ -19,6 +19,7 @@ typedef std::string String; void digitalWrite(uint8_t pin, uint8_t val); int digitalRead(uint8_t pin); +void analogWrite(uint8_t pin, int val); void pinMode(uint8_t pin, uint8_t mode); unsigned long millis(); diff --git a/extras/test/doubles/arduino_mock.cpp b/extras/test/doubles/arduino_mock.cpp index f64576a2..981447ac 100644 --- a/extras/test/doubles/arduino_mock.cpp +++ b/extras/test/doubles/arduino_mock.cpp @@ -41,6 +41,10 @@ TimeInterface::~TimeInterface() { TimeInterface *TimeInterface::instance = nullptr; +void analogWrite(uint8_t pin, int val) { + DigitalInterface::instance->analogWrite(pin, val); +} + void digitalWrite(uint8_t pin, uint8_t val) { DigitalInterface::instance->digitalWrite(pin, val); } diff --git a/extras/test/doubles/arduino_mock.h b/extras/test/doubles/arduino_mock.h index 8daf5a1e..01f7d372 100644 --- a/extras/test/doubles/arduino_mock.h +++ b/extras/test/doubles/arduino_mock.h @@ -26,6 +26,7 @@ class DigitalInterface { virtual ~DigitalInterface(); virtual void digitalWrite(uint8_t, uint8_t) = 0; virtual int digitalRead(uint8_t) = 0; + virtual void analogWrite(uint8_t pin, int val) = 0; virtual void pinMode(uint8_t, uint8_t) = 0; static DigitalInterface *instance; @@ -44,6 +45,7 @@ class TimeInterface { class DigitalInterfaceMock : public DigitalInterface { public: MOCK_METHOD(void, digitalWrite, (uint8_t, uint8_t), (override)); + MOCK_METHOD(void, analogWrite, (uint8_t, int), (override)); MOCK_METHOD(int, digitalRead, (uint8_t), (override)); MOCK_METHOD(void, pinMode, (uint8_t, uint8_t), (override)); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e579e930..fbfd6466 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,9 @@ set(SRCS supla/control/rgbw_base.cpp supla/control/rgb_base.cpp supla/control/dimmer_base.cpp + supla/control/rgbw_leds.cpp + supla/control/rgb_leds.cpp + supla/control/dimmer_leds.cpp supla/condition.cpp supla/conditions/on_less.cpp diff --git a/src/supla/control/dimmer_leds.cpp b/src/supla/control/dimmer_leds.cpp new file mode 100644 index 00000000..7f19213f --- /dev/null +++ b/src/supla/control/dimmer_leds.cpp @@ -0,0 +1,39 @@ +/* +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 "dimmer_leds.h" + +Supla::Control::DimmerLeds::DimmerLeds(int brightnessPin) + : brightnessPin(brightnessPin) { +} + +void Supla::Control::DimmerLeds::setRGBWValueOnDevice(uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t colorBrightness, + uint8_t brightness) { + analogWrite(brightnessPin, (brightness * 255) / 100); +} + +void Supla::Control::DimmerLeds::onInit() { + pinMode(brightnessPin, OUTPUT); + +#ifdef ARDUINO_ARCH_ESP8266 + analogWriteRange(255); +#endif + + Supla::Control::DimmerBase::onInit(); +} diff --git a/src/supla/control/dimmer_leds.h b/src/supla/control/dimmer_leds.h new file mode 100644 index 00000000..7fed3d48 --- /dev/null +++ b/src/supla/control/dimmer_leds.h @@ -0,0 +1,44 @@ +/* +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 __dimmer_leds_h +#define __dimmer_leds_h + +#include "dimmer_base.h" + +namespace Supla { +namespace Control { +class DimmerLeds : public DimmerBase { + public: + DimmerLeds(int brightnessPin); + + void setRGBWValueOnDevice(uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t colorBrightness, + uint8_t brightness); + + void onInit(); + + protected: + int brightnessPin; +}; + +}; // namespace Control +}; // namespace Supla + +#endif + diff --git a/src/supla/control/rgb_leds.cpp b/src/supla/control/rgb_leds.cpp new file mode 100644 index 00000000..665454dc --- /dev/null +++ b/src/supla/control/rgb_leds.cpp @@ -0,0 +1,52 @@ +/* +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 "rgb_leds.h" + +Supla::Control::RGBLeds::RGBLeds(int redPin, + int greenPin, + int bluePin, + int colorBrightnessPin) + : redPin(redPin), + greenPin(greenPin), + bluePin(bluePin), + colorBrightnessPin(colorBrightnessPin) +{} + +void Supla::Control::RGBLeds::setRGBWValueOnDevice(uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t colorBrightness, + uint8_t brightness) { + analogWrite(redPin, red); + analogWrite(greenPin, green); + analogWrite(bluePin, blue); + analogWrite(colorBrightnessPin, (colorBrightness * 255) / 100); +} + +void Supla::Control::RGBLeds::onInit() { + pinMode(redPin, OUTPUT); + pinMode(greenPin, OUTPUT); + pinMode(bluePin, OUTPUT); + pinMode(colorBrightnessPin, OUTPUT); + + #ifdef ARDUINO_ARCH_ESP8266 + analogWriteRange(255); + #endif + + Supla::Control::RGBBase::onInit(); +} + diff --git a/src/supla/control/rgb_leds.h b/src/supla/control/rgb_leds.h new file mode 100644 index 00000000..f8411552 --- /dev/null +++ b/src/supla/control/rgb_leds.h @@ -0,0 +1,46 @@ +/* +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 __rgb_leds_h +#define __rgb_leds_h + +#include "rgb_base.h" + +namespace Supla { +namespace Control { +class RGBLeds : public RGBBase { + public: + RGBLeds(int redPin, int greenPin, int bluePin, int colorBrightnessPin); + + void setRGBWValueOnDevice(uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t colorBrightness, + uint8_t brightness); + + void onInit(); + + protected: + int redPin; + int greenPin; + int bluePin; + int colorBrightnessPin; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/src/supla/control/rgbw_leds.cpp b/src/supla/control/rgbw_leds.cpp new file mode 100644 index 00000000..d85d24e6 --- /dev/null +++ b/src/supla/control/rgbw_leds.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 "rgbw_leds.h" + +Supla::Control::RGBWLeds::RGBWLeds(int redPin, + int greenPin, + int bluePin, + int colorBrightnessPin, + int brightnessPin) + : redPin(redPin), + greenPin(greenPin), + bluePin(bluePin), + colorBrightnessPin(colorBrightnessPin), + brightnessPin(brightnessPin) { +} + +void Supla::Control::RGBWLeds::setRGBWValueOnDevice(uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t colorBrightness, + uint8_t brightness) { + analogWrite(redPin, red); + analogWrite(greenPin, green); + analogWrite(bluePin, blue); + analogWrite(brightnessPin, (brightness * 255) / 100); + analogWrite(colorBrightnessPin, (colorBrightness * 255) / 100); +} + +void Supla::Control::RGBWLeds::onInit() { + pinMode(redPin, OUTPUT); + pinMode(greenPin, OUTPUT); + pinMode(bluePin, OUTPUT); + pinMode(colorBrightnessPin, OUTPUT); + pinMode(brightnessPin, OUTPUT); + + #ifdef ARDUINO_ARCH_ESP8266 + analogWriteRange(255); + #endif + + Supla::Control::RGBWBase::onInit(); +} diff --git a/src/supla/control/rgbw_leds.h b/src/supla/control/rgbw_leds.h new file mode 100644 index 00000000..62e8c402 --- /dev/null +++ b/src/supla/control/rgbw_leds.h @@ -0,0 +1,51 @@ +/* +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 __rgbw_leds_h +#define __rgbw_leds_h + +#include "rgbw_base.h" + +namespace Supla { +namespace Control { +class RGBWLeds : public RGBWBase { + public: + RGBWLeds(int redPin, + int greenPin, + int bluePin, + int colorBrightnessPin, + int brightnessPin); + + void setRGBWValueOnDevice(uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t colorBrightness, + uint8_t brightness); + + void onInit(); + + protected: + int redPin; + int greenPin; + int bluePin; + int brightnessPin; + int colorBrightnessPin; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/src/supla/storage/storage.cpp b/src/supla/storage/storage.cpp index c9f9d974..9f78faf0 100644 --- a/src/supla/storage/storage.cpp +++ b/src/supla/storage/storage.cpp @@ -107,6 +107,10 @@ Storage::Storage(unsigned int storageStartingOffset) instance = this; } +Storage::~Storage() { + instance = nullptr; +} + void Storage::prepareState(bool performDryRun) { dryRun = performDryRun; newSectionSize = 0; diff --git a/src/supla/storage/storage.h b/src/supla/storage/storage.h index f25a4091..0537749c 100644 --- a/src/supla/storage/storage.h +++ b/src/supla/storage/storage.h @@ -39,6 +39,7 @@ class Storage { static void ScheduleSave(unsigned long delayMs); Storage(unsigned int storageStartingOffset = 0); + virtual ~Storage(); // Changes default state save period time virtual void setStateSavePeriod(unsigned long periodMs); From 59f077805390bac85a3886a890e8653418781a31 Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 29 Jan 2021 13:21:50 +0100 Subject: [PATCH 07/38] Added ESP32 specific "analogWrite" implementation for RGBW, RGB, Dimmer Leds elements --- examples/RGBW/RGBW.ino | 38 ++----------------------- src/supla/control/dimmer_leds.cpp | 22 +++++++++++++++ src/supla/control/rgb_leds.cpp | 40 ++++++++++++++++++++++++++ src/supla/control/rgbw_base.cpp | 5 ++++ src/supla/control/rgbw_leds.cpp | 47 +++++++++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 36 deletions(-) diff --git a/examples/RGBW/RGBW.ino b/examples/RGBW/RGBW.ino index fb446e08..54742346 100644 --- a/examples/RGBW/RGBW.ino +++ b/examples/RGBW/RGBW.ino @@ -15,7 +15,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include -#include +#include // Choose proper network interface for your card: #ifdef ARDUINO_ARCH_AVR @@ -45,40 +45,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define BRIGHTNESS_PIN 7 #define COLOR_BRIGHTNESS_PIN 8 -class RgbwLeds : public Supla::Control::RGBWBase { - public: - RgbwLeds(int redPin, - int greenPin, - int bluePin, - int colorBrightnessPin, - int brightnessPin) - : redPin(redPin), - greenPin(greenPin), - bluePin(bluePin), - colorBrightnessPin(colorBrightnessPin), - brightnessPin(brightnessPin) { - } - - void setRGBWValueOnDevice(uint8_t red, - uint8_t green, - uint8_t blue, - uint8_t colorBrightness, - uint8_t brightness) { - analogWrite(brightnessPin, (brightness * 255) / 100); - analogWrite(colorBrightnessPin, (colorBrightness * 255) / 100); - analogWrite(redPin, red); - analogWrite(greenPin, green); - analogWrite(bluePin, blue); - } - - protected: - int redPin; - int greenPin; - int bluePin; - int brightnessPin; - int colorBrightnessPin; -}; - void setup() { Serial.begin(115200); @@ -96,7 +62,7 @@ void setup() { */ // CHANNEL0 - RGB controller and dimmer (RGBW) - new RgbwLeds( + new Supla::Control::RGBWLeds( RED_PIN, GREEN_PIN, BLUE_PIN, COLOR_BRIGHTNESS_PIN, BRIGHTNESS_PIN); /* diff --git a/src/supla/control/dimmer_leds.cpp b/src/supla/control/dimmer_leds.cpp index 7f19213f..d6a3f6bb 100644 --- a/src/supla/control/dimmer_leds.cpp +++ b/src/supla/control/dimmer_leds.cpp @@ -16,6 +16,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "dimmer_leds.h" +#ifdef ARDUINO_ARCH_ESP32 +extern int esp32PwmChannelCouner; +#endif + Supla::Control::DimmerLeds::DimmerLeds(int brightnessPin) : brightnessPin(brightnessPin) { } @@ -25,14 +29,32 @@ void Supla::Control::DimmerLeds::setRGBWValueOnDevice(uint8_t red, uint8_t blue, uint8_t colorBrightness, uint8_t brightness) { +#ifdef ARDUINO_ARCH_ESP32 + ledcWrite(brightnessPin, (brightness * 255) / 100); +#else analogWrite(brightnessPin, (brightness * 255) / 100); +#endif } void Supla::Control::DimmerLeds::onInit() { +#ifdef ARDUINO_ARCH_ESP32 + Serial.print(F("Dimmer: attaching pin ")); + Serial.print(brightnessPin); + Serial.print(F(" to PWM channel: ")); + Serial.println(esp32PwmChannelCouner); + + ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcAttachPin(brightnessPin, esp32PwmChannelCouner); + // on ESP32 we write to PWM channels instead of pins, so we copy channel + // number as pin in order to reuse variable + brightnessPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; +#else pinMode(brightnessPin, OUTPUT); #ifdef ARDUINO_ARCH_ESP8266 analogWriteRange(255); +#endif #endif Supla::Control::DimmerBase::onInit(); diff --git a/src/supla/control/rgb_leds.cpp b/src/supla/control/rgb_leds.cpp index 665454dc..1e0dfdd1 100644 --- a/src/supla/control/rgb_leds.cpp +++ b/src/supla/control/rgb_leds.cpp @@ -16,6 +16,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "rgb_leds.h" +#ifdef ARDUINO_ARCH_ESP32 +extern int esp32PwmChannelCouner; +#endif + Supla::Control::RGBLeds::RGBLeds(int redPin, int greenPin, int bluePin, @@ -31,13 +35,48 @@ void Supla::Control::RGBLeds::setRGBWValueOnDevice(uint8_t red, uint8_t blue, uint8_t colorBrightness, uint8_t brightness) { +#ifdef ARDUINO_ARCH_ESP32 + ledcWrite(redPin, red); + ledcWrite(greenPin, green); + ledcWrite(bluePin, blue); + ledcWrite(colorBrightnessPin, (colorBrightness * 255) / 100); +#else analogWrite(redPin, red); analogWrite(greenPin, green); analogWrite(bluePin, blue); analogWrite(colorBrightnessPin, (colorBrightness * 255) / 100); +#endif } void Supla::Control::RGBLeds::onInit() { +#ifdef ARDUINO_ARCH_ESP32 + Serial.print(F("RGB: attaching pin ")); + Serial.print(redPin); + Serial.print(F(" to PWM channel: ")); + Serial.println(esp32PwmChannelCouner); + + ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcAttachPin(redPin, esp32PwmChannelCouner); + // on ESP32 we write to PWM channels instead of pins, so we copy channel + // number as pin in order to reuse variable + redPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcAttachPin(greenPin, esp32PwmChannelCouner); + greenPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcAttachPin(bluePin, esp32PwmChannelCouner); + bluePin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcAttachPin(colorBrightnessPin, esp32PwmChannelCouner); + colorBrightnessPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; +#else pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); @@ -46,6 +85,7 @@ void Supla::Control::RGBLeds::onInit() { #ifdef ARDUINO_ARCH_ESP8266 analogWriteRange(255); #endif +#endif Supla::Control::RGBBase::onInit(); } diff --git a/src/supla/control/rgbw_base.cpp b/src/supla/control/rgbw_base.cpp index aa2da0b2..70cbf258 100644 --- a/src/supla/control/rgbw_base.cpp +++ b/src/supla/control/rgbw_base.cpp @@ -24,6 +24,11 @@ #define RGBW_STATE_ON_INIT_OFF 0 #define RGBW_STATE_ON_INIT_ON 1 +#ifdef ARDUINO_ARCH_ESP32 + int esp32PwmChannelCouner = 0; +#endif + + namespace Supla { namespace Control { diff --git a/src/supla/control/rgbw_leds.cpp b/src/supla/control/rgbw_leds.cpp index d85d24e6..609c8501 100644 --- a/src/supla/control/rgbw_leds.cpp +++ b/src/supla/control/rgbw_leds.cpp @@ -16,6 +16,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "rgbw_leds.h" +#ifdef ARDUINO_ARCH_ESP32 +extern int esp32PwmChannelCouner; +#endif + Supla::Control::RGBWLeds::RGBWLeds(int redPin, int greenPin, int bluePin, @@ -33,14 +37,56 @@ void Supla::Control::RGBWLeds::setRGBWValueOnDevice(uint8_t red, uint8_t blue, uint8_t colorBrightness, uint8_t brightness) { +#ifdef ARDUINO_ARCH_ESP32 + ledcWrite(redPin, red); + ledcWrite(greenPin, green); + ledcWrite(bluePin, blue); + ledcWrite(colorBrightnessPin, (colorBrightness * 255) / 100); + ledcWrite(brightnessPin, (brightness * 255) / 100); +#else analogWrite(redPin, red); analogWrite(greenPin, green); analogWrite(bluePin, blue); analogWrite(brightnessPin, (brightness * 255) / 100); analogWrite(colorBrightnessPin, (colorBrightness * 255) / 100); +#endif } void Supla::Control::RGBWLeds::onInit() { +#ifdef ARDUINO_ARCH_ESP32 + Serial.print(F("RGBW: attaching pin ")); + Serial.print(redPin); + Serial.print(F(" to PWM channel: ")); + Serial.println(esp32PwmChannelCouner); + + ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcAttachPin(redPin, esp32PwmChannelCouner); + // on ESP32 we write to PWM channels instead of pins, so we copy channel + // number as pin in order to reuse variable + redPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcAttachPin(greenPin, esp32PwmChannelCouner); + greenPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcAttachPin(bluePin, esp32PwmChannelCouner); + bluePin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcAttachPin(colorBrightnessPin, esp32PwmChannelCouner); + colorBrightnessPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcAttachPin(brightnessPin, esp32PwmChannelCouner); + brightnessPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + +#else pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); @@ -50,6 +96,7 @@ void Supla::Control::RGBWLeds::onInit() { #ifdef ARDUINO_ARCH_ESP8266 analogWriteRange(255); #endif +#endif Supla::Control::RGBWBase::onInit(); } From cf1fa227e1bc969db9cdcdff375ced3f4a3c7d8b Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 29 Jan 2021 13:41:03 +0100 Subject: [PATCH 08/38] Added Button to RGBW example --- examples/RGBW/RGBW.ino | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/examples/RGBW/RGBW.ino b/examples/RGBW/RGBW.ino index 54742346..c400c76a 100644 --- a/examples/RGBW/RGBW.ino +++ b/examples/RGBW/RGBW.ino @@ -16,6 +16,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include +#include // Choose proper network interface for your card: #ifdef ARDUINO_ARCH_AVR @@ -39,11 +40,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Youtube example was done on older version of SuplaDevice library */ -#define RED_PIN 44 -#define GREEN_PIN 45 -#define BLUE_PIN 46 -#define BRIGHTNESS_PIN 7 -#define COLOR_BRIGHTNESS_PIN 8 +#define RED_PIN 4 +#define GREEN_PIN 5 +#define BLUE_PIN 12 +#define BRIGHTNESS_PIN 13 +#define COLOR_BRIGHTNESS_PIN 14 +#define BUTTON_PIN 0 void setup() { Serial.begin(115200); @@ -62,9 +64,17 @@ void setup() { */ // CHANNEL0 - RGB controller and dimmer (RGBW) - new Supla::Control::RGBWLeds( + auto rgbw = new Supla::Control::RGBWLeds( RED_PIN, GREEN_PIN, BLUE_PIN, COLOR_BRIGHTNESS_PIN, BRIGHTNESS_PIN); + auto button = new Supla::Control::Button(BUTTON_PIN, true, true); + button->setMulticlickTime(200); + button->setHoldTime(400); + button->repeatOnHoldEvery(200); + + button->addAction(Supla::ITERATE_DIM_ALL, rgbw, Supla::ON_HOLD); + button->addAction(Supla::TOGGLE, rgbw, Supla::ON_CLICK_1); + /* * SuplaDevice Initialization. * Server address is available at https://cloud.supla.org From e76419cc8c6f1f6e6f2363e2b7118223cc9cdc2a Mon Sep 17 00:00:00 2001 From: klew Date: Sun, 31 Jan 2021 23:28:57 +0100 Subject: [PATCH 09/38] Fixed ITERATE_DIM_ALL for Dimmer --- extras/test/RgbwDimmerTests/dimmer_tests.cpp | 53 ++++++++++++++++++ .../test/RgbwDimmerTests/rgb_base_tests.cpp | 54 +++++++++++++++++++ src/supla/control/dimmer_base.cpp | 4 ++ src/supla/control/dimmer_base.h | 3 ++ src/supla/control/rgbw_base.h | 2 +- 5 files changed, 115 insertions(+), 1 deletion(-) diff --git a/extras/test/RgbwDimmerTests/dimmer_tests.cpp b/extras/test/RgbwDimmerTests/dimmer_tests.cpp index c9b90c8f..db80823e 100644 --- a/extras/test/RgbwDimmerTests/dimmer_tests.cpp +++ b/extras/test/RgbwDimmerTests/dimmer_tests.cpp @@ -117,4 +117,57 @@ TEST(DimmerTests, DimmerShouldIgnoreRGBValues) { } +TEST(DimmerTests, HandleActionTests) { + // time stub will return +1000 ms on each call to millis + TimeInterfaceStub time; + + DimmerBaseForTest dimmer; + + auto ch = dimmer.getChannel(); + + dimmer.setStep(10); + dimmer.onInit(); + dimmer.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + dimmer.handleAction(1, Supla::ITERATE_DIM_ALL); + dimmer.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 10); + + dimmer.handleAction(1, Supla::ITERATE_DIM_ALL); + dimmer.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 20); + + dimmer.handleAction(1, Supla::ITERATE_DIM_ALL); + dimmer.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 30); + + dimmer.handleAction(1, Supla::ITERATE_DIM_ALL); + dimmer.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 0); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 40); + + +} + diff --git a/extras/test/RgbwDimmerTests/rgb_base_tests.cpp b/extras/test/RgbwDimmerTests/rgb_base_tests.cpp index b641a82c..974a70fa 100644 --- a/extras/test/RgbwDimmerTests/rgb_base_tests.cpp +++ b/extras/test/RgbwDimmerTests/rgb_base_tests.cpp @@ -117,3 +117,57 @@ TEST(RgbTests, RgbShouldIgnoreBrightnessValue) { } +TEST(RgbTests, HandleActionTests) { + // time stub will return +1000 ms on each call to millis + TimeInterfaceStub time; + + RgbBaseForTest rgb; + + auto ch = rgb.getChannel(); + + rgb.setStep(10); + rgb.onInit(); + rgb.iterateAlways(); + + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 0); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::ITERATE_DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 10); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::ITERATE_DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 20); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::ITERATE_DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 30); + EXPECT_EQ(ch->getValueBrightness(), 0); + + rgb.handleAction(1, Supla::ITERATE_DIM_ALL); + rgb.iterateAlways(); + EXPECT_EQ(ch->getValueRed(), 0); + EXPECT_EQ(ch->getValueGreen(), 255); + EXPECT_EQ(ch->getValueBlue(), 0); + EXPECT_EQ(ch->getValueColorBrightness(), 40); + EXPECT_EQ(ch->getValueBrightness(), 0); + + +} + + diff --git a/src/supla/control/dimmer_base.cpp b/src/supla/control/dimmer_base.cpp index 5c01e57b..88f6f1e6 100644 --- a/src/supla/control/dimmer_base.cpp +++ b/src/supla/control/dimmer_base.cpp @@ -42,3 +42,7 @@ void Supla::Control::DimmerBase::onSaveState() { sizeof(curBrightness)); Supla::Storage::WriteState((unsigned char *)&lastBrightness, sizeof(lastBrightness)); } + +void Supla::Control::DimmerBase::iterateDimmerRGBW(int rgbStep, int wStep) { + Supla::Control::RGBWBase::iterateDimmerRGBW(0, wStep); +} diff --git a/src/supla/control/dimmer_base.h b/src/supla/control/dimmer_base.h index 2d38da1d..33fde7e9 100644 --- a/src/supla/control/dimmer_base.h +++ b/src/supla/control/dimmer_base.h @@ -36,6 +36,9 @@ class DimmerBase : public RGBWBase { void onLoadState(); void onSaveState(); + protected: + virtual void iterateDimmerRGBW(int rgbStep, int wStep); + }; }; // namespace Control diff --git a/src/supla/control/rgbw_base.h b/src/supla/control/rgbw_base.h index 53429dfc..5f3790fd 100644 --- a/src/supla/control/rgbw_base.h +++ b/src/supla/control/rgbw_base.h @@ -64,7 +64,7 @@ class RGBWBase : public ChannelElement, public ActionHandler { protected: uint8_t addWithLimit(int value, int addition, int limit = 255); - void iterateDimmerRGBW(int rgbStep, int wStep); + virtual void iterateDimmerRGBW(int rgbStep, int wStep); uint8_t buttonStep; // 10 uint8_t curRed; // 0 - 255 From 86ecf38998e7fab2dcbd18f692d097fd1c05dd6d Mon Sep 17 00:00:00 2001 From: klew Date: Sat, 6 Feb 2021 22:30:43 +0100 Subject: [PATCH 10/38] Fix for roller shutters with opening time >32s on Arduino Mega --- src/supla/control/roller_shutter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/supla/control/roller_shutter.cpp b/src/supla/control/roller_shutter.cpp index 237b54aa..c249ef34 100644 --- a/src/supla/control/roller_shutter.cpp +++ b/src/supla/control/roller_shutter.cpp @@ -392,7 +392,7 @@ void RollerShutter::onTimer() { // just handle roller movement/status if (currentDirection == UP_DIR && currentPosition > 0) { int movementDistance = lastPositionBeforeMovement; - int timeRequired = (1.0 * openingTimeMs * movementDistance / 100.0); + uint32_t timeRequired = (1.0 * openingTimeMs * movementDistance / 100.0); float fractionOfMovemendDone = (1.0 * (millis() - lastMovementStartTime) / timeRequired); if (fractionOfMovemendDone > 1) { @@ -405,7 +405,7 @@ void RollerShutter::onTimer() { } } else if (currentDirection == DOWN_DIR && currentPosition < 100) { int movementDistance = 100 - lastPositionBeforeMovement; - int timeRequired = (1.0 * closingTimeMs * movementDistance / 100.0); + uint32_t timeRequired = (1.0 * closingTimeMs * movementDistance / 100.0); float fractionOfMovemendDone = (1.0 * (millis() - lastMovementStartTime) / timeRequired); if (fractionOfMovemendDone > 1) { From 243ef0bdb56911673cd02bb7c02feb3573773c26 Mon Sep 17 00:00:00 2001 From: klew Date: Mon, 8 Feb 2021 14:31:41 +0100 Subject: [PATCH 11/38] Modified RGBW classes so they will not use separate colorBrightness pin Changed PWM resolution for ESP32 and ESP82xx boards to 10 bits. --- examples/RGBW/RGBW.ino | 3 +- .../test/RgbwDimmerTests/rgb_leds_tests.cpp | 11 ++--- .../test/RgbwDimmerTests/rgbw_leds_test.cpp | 15 +++--- src/supla/control/dimmer_leds.cpp | 17 +++++-- src/supla/control/rgb_leds.cpp | 45 +++++++++-------- src/supla/control/rgb_leds.h | 3 +- src/supla/control/rgbw_leds.cpp | 49 ++++++++++--------- src/supla/control/rgbw_leds.h | 2 - 8 files changed, 75 insertions(+), 70 deletions(-) diff --git a/examples/RGBW/RGBW.ino b/examples/RGBW/RGBW.ino index c400c76a..35d3f16e 100644 --- a/examples/RGBW/RGBW.ino +++ b/examples/RGBW/RGBW.ino @@ -44,7 +44,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define GREEN_PIN 5 #define BLUE_PIN 12 #define BRIGHTNESS_PIN 13 -#define COLOR_BRIGHTNESS_PIN 14 #define BUTTON_PIN 0 void setup() { @@ -65,7 +64,7 @@ void setup() { // CHANNEL0 - RGB controller and dimmer (RGBW) auto rgbw = new Supla::Control::RGBWLeds( - RED_PIN, GREEN_PIN, BLUE_PIN, COLOR_BRIGHTNESS_PIN, BRIGHTNESS_PIN); + RED_PIN, GREEN_PIN, BLUE_PIN, BRIGHTNESS_PIN); auto button = new Supla::Control::Button(BUTTON_PIN, true, true); button->setMulticlickTime(200); diff --git a/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp b/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp index 02bd948b..9c0e6c08 100644 --- a/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp +++ b/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp @@ -37,18 +37,15 @@ TEST(RgbLedsTests, SettingNewRGBValue) { EXPECT_CALL(ioMock, pinMode(1, OUTPUT)); EXPECT_CALL(ioMock, pinMode(2, OUTPUT)); EXPECT_CALL(ioMock, pinMode(3, OUTPUT)); - EXPECT_CALL(ioMock, pinMode(4, OUTPUT)); EXPECT_CALL(ioMock, analogWrite(1, 0)); - EXPECT_CALL(ioMock, analogWrite(2, 255)); + EXPECT_CALL(ioMock, analogWrite(2, 0)); EXPECT_CALL(ioMock, analogWrite(3, 0)); - EXPECT_CALL(ioMock, analogWrite(4, 0)); EXPECT_CALL(ioMock, analogWrite(1, 1)); EXPECT_CALL(ioMock, analogWrite(2, 2)); EXPECT_CALL(ioMock, analogWrite(3, 3)); - EXPECT_CALL(ioMock, analogWrite(4, (255*4)/100)); - Supla::Control::RGBLeds rgbw(1, 2, 3, 4); + Supla::Control::RGBLeds rgbw(1, 2, 3); auto ch = rgbw.getChannel(); // disable fading effect so we'll get instant setting value on device call @@ -76,14 +73,14 @@ TEST(RgbLedsTests, SettingNewRGBValue) { EXPECT_EQ(ch->getValueColorBrightness(), 0); EXPECT_EQ(ch->getValueBrightness(), 0); - rgbw.setRGBW(1, 2, 3, 4, 5); + rgbw.setRGBW(1, 2, 3, 100, 5); rgbw.iterateAlways(); EXPECT_EQ(ch->getValueRed(), 1); EXPECT_EQ(ch->getValueGreen(), 2); EXPECT_EQ(ch->getValueBlue(), 3); - EXPECT_EQ(ch->getValueColorBrightness(), 4); + EXPECT_EQ(ch->getValueColorBrightness(), 100); EXPECT_EQ(ch->getValueBrightness(), 0); } diff --git a/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp b/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp index 09aca654..02e76374 100644 --- a/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp +++ b/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp @@ -38,20 +38,17 @@ TEST(RgbwLedsTests, SettingNewRGBWValue) { EXPECT_CALL(ioMock, pinMode(2, OUTPUT)); EXPECT_CALL(ioMock, pinMode(3, OUTPUT)); EXPECT_CALL(ioMock, pinMode(4, OUTPUT)); - EXPECT_CALL(ioMock, pinMode(5, OUTPUT)); EXPECT_CALL(ioMock, analogWrite(1, 0)); - EXPECT_CALL(ioMock, analogWrite(2, 255)); + EXPECT_CALL(ioMock, analogWrite(2, 0)); EXPECT_CALL(ioMock, analogWrite(3, 0)); EXPECT_CALL(ioMock, analogWrite(4, 0)); - EXPECT_CALL(ioMock, analogWrite(5, 0)); EXPECT_CALL(ioMock, analogWrite(1, 1)); EXPECT_CALL(ioMock, analogWrite(2, 2)); EXPECT_CALL(ioMock, analogWrite(3, 3)); - EXPECT_CALL(ioMock, analogWrite(4, (255*4)/100)); - EXPECT_CALL(ioMock, analogWrite(5, (255*5)/100)); + EXPECT_CALL(ioMock, analogWrite(4, 255)); - Supla::Control::RGBWLeds rgbw(1, 2, 3, 4, 5); + Supla::Control::RGBWLeds rgbw(1, 2, 3, 4); auto ch = rgbw.getChannel(); // disable fading effect so we'll get instant setting value on device call @@ -79,15 +76,15 @@ TEST(RgbwLedsTests, SettingNewRGBWValue) { EXPECT_EQ(ch->getValueColorBrightness(), 0); EXPECT_EQ(ch->getValueBrightness(), 0); - rgbw.setRGBW(1, 2, 3, 4, 5); + rgbw.setRGBW(1, 2, 3, 100, 100); rgbw.iterateAlways(); EXPECT_EQ(ch->getValueRed(), 1); EXPECT_EQ(ch->getValueGreen(), 2); EXPECT_EQ(ch->getValueBlue(), 3); - EXPECT_EQ(ch->getValueColorBrightness(), 4); - EXPECT_EQ(ch->getValueBrightness(), 5); + EXPECT_EQ(ch->getValueColorBrightness(), 100); + EXPECT_EQ(ch->getValueBrightness(), 100); } diff --git a/src/supla/control/dimmer_leds.cpp b/src/supla/control/dimmer_leds.cpp index d6a3f6bb..13781eb1 100644 --- a/src/supla/control/dimmer_leds.cpp +++ b/src/supla/control/dimmer_leds.cpp @@ -29,10 +29,19 @@ void Supla::Control::DimmerLeds::setRGBWValueOnDevice(uint8_t red, uint8_t blue, uint8_t colorBrightness, uint8_t brightness) { + int multiplier = 1; +#ifdef ARDUINO_ARCH_ESP8266 + multiplier = 4; +#endif +#ifdef ARDUINO_ARCH_ESP32 + multiplier = 4; +#endif + + int brightnessAdj = brightness * multiplier * 255 / 100; #ifdef ARDUINO_ARCH_ESP32 - ledcWrite(brightnessPin, (brightness * 255) / 100); + ledcWrite(brightnessPin, brightnessAdj); #else - analogWrite(brightnessPin, (brightness * 255) / 100); + analogWrite(brightnessPin, brightnessAdj); #endif } @@ -43,7 +52,7 @@ void Supla::Control::DimmerLeds::onInit() { Serial.print(F(" to PWM channel: ")); Serial.println(esp32PwmChannelCouner); - ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcSetup(esp32PwmChannelCouner, 12000, 10); ledcAttachPin(brightnessPin, esp32PwmChannelCouner); // on ESP32 we write to PWM channels instead of pins, so we copy channel // number as pin in order to reuse variable @@ -53,7 +62,7 @@ void Supla::Control::DimmerLeds::onInit() { pinMode(brightnessPin, OUTPUT); #ifdef ARDUINO_ARCH_ESP8266 - analogWriteRange(255); + analogWriteRange(1024); #endif #endif diff --git a/src/supla/control/rgb_leds.cpp b/src/supla/control/rgb_leds.cpp index 1e0dfdd1..b0ff0979 100644 --- a/src/supla/control/rgb_leds.cpp +++ b/src/supla/control/rgb_leds.cpp @@ -22,12 +22,10 @@ extern int esp32PwmChannelCouner; Supla::Control::RGBLeds::RGBLeds(int redPin, int greenPin, - int bluePin, - int colorBrightnessPin) + int bluePin) : redPin(redPin), greenPin(greenPin), - bluePin(bluePin), - colorBrightnessPin(colorBrightnessPin) + bluePin(bluePin) {} void Supla::Control::RGBLeds::setRGBWValueOnDevice(uint8_t red, @@ -35,16 +33,26 @@ void Supla::Control::RGBLeds::setRGBWValueOnDevice(uint8_t red, uint8_t blue, uint8_t colorBrightness, uint8_t brightness) { + int multiplier = 1; +#ifdef ARDUINO_ARCH_ESP8266 + multiplier = 4; +#endif +#ifdef ARDUINO_ARCH_ESP32 + multiplier = 4; +#endif + + int redAdj = red * multiplier * colorBrightness / 100; + int greenAdj = green* multiplier * colorBrightness / 100; + int blueAdj = blue * multiplier * colorBrightness / 100; + #ifdef ARDUINO_ARCH_ESP32 - ledcWrite(redPin, red); - ledcWrite(greenPin, green); - ledcWrite(bluePin, blue); - ledcWrite(colorBrightnessPin, (colorBrightness * 255) / 100); + ledcWrite(redPin, redAdj); + ledcWrite(greenPin, greenAdj); + ledcWrite(bluePin, blueAdj); #else - analogWrite(redPin, red); - analogWrite(greenPin, green); - analogWrite(bluePin, blue); - analogWrite(colorBrightnessPin, (colorBrightness * 255) / 100); + analogWrite(redPin, redAdj); + analogWrite(greenPin, greenAdj); + analogWrite(bluePin, blueAdj); #endif } @@ -55,35 +63,30 @@ void Supla::Control::RGBLeds::onInit() { Serial.print(F(" to PWM channel: ")); Serial.println(esp32PwmChannelCouner); - ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcSetup(esp32PwmChannelCouner, 12000, 10); ledcAttachPin(redPin, esp32PwmChannelCouner); // on ESP32 we write to PWM channels instead of pins, so we copy channel // number as pin in order to reuse variable redPin = esp32PwmChannelCouner; esp32PwmChannelCouner++; - ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcSetup(esp32PwmChannelCouner, 12000, 10); ledcAttachPin(greenPin, esp32PwmChannelCouner); greenPin = esp32PwmChannelCouner; esp32PwmChannelCouner++; - ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcSetup(esp32PwmChannelCouner, 12000, 10); ledcAttachPin(bluePin, esp32PwmChannelCouner); bluePin = esp32PwmChannelCouner; esp32PwmChannelCouner++; - ledcSetup(esp32PwmChannelCouner, 12000, 8); - ledcAttachPin(colorBrightnessPin, esp32PwmChannelCouner); - colorBrightnessPin = esp32PwmChannelCouner; - esp32PwmChannelCouner++; #else pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); - pinMode(colorBrightnessPin, OUTPUT); #ifdef ARDUINO_ARCH_ESP8266 - analogWriteRange(255); + analogWriteRange(1024); #endif #endif diff --git a/src/supla/control/rgb_leds.h b/src/supla/control/rgb_leds.h index f8411552..372052c5 100644 --- a/src/supla/control/rgb_leds.h +++ b/src/supla/control/rgb_leds.h @@ -23,7 +23,7 @@ namespace Supla { namespace Control { class RGBLeds : public RGBBase { public: - RGBLeds(int redPin, int greenPin, int bluePin, int colorBrightnessPin); + RGBLeds(int redPin, int greenPin, int bluePin); void setRGBWValueOnDevice(uint8_t red, uint8_t green, @@ -37,7 +37,6 @@ class RGBLeds : public RGBBase { int redPin; int greenPin; int bluePin; - int colorBrightnessPin; }; }; // namespace Control diff --git a/src/supla/control/rgbw_leds.cpp b/src/supla/control/rgbw_leds.cpp index 609c8501..1e11a112 100644 --- a/src/supla/control/rgbw_leds.cpp +++ b/src/supla/control/rgbw_leds.cpp @@ -15,6 +15,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "rgbw_leds.h" +#include #ifdef ARDUINO_ARCH_ESP32 extern int esp32PwmChannelCouner; @@ -23,12 +24,10 @@ extern int esp32PwmChannelCouner; Supla::Control::RGBWLeds::RGBWLeds(int redPin, int greenPin, int bluePin, - int colorBrightnessPin, int brightnessPin) : redPin(redPin), greenPin(greenPin), bluePin(bluePin), - colorBrightnessPin(colorBrightnessPin), brightnessPin(brightnessPin) { } @@ -37,18 +36,28 @@ void Supla::Control::RGBWLeds::setRGBWValueOnDevice(uint8_t red, uint8_t blue, uint8_t colorBrightness, uint8_t brightness) { + int multiplier = 1; +#ifdef ARDUINO_ARCH_ESP8266 + multiplier = 4; +#endif +#ifdef ARDUINO_ARCH_ESP32 + multiplier = 4; +#endif + + int redAdj = red * multiplier * colorBrightness / 100; + int greenAdj = green* multiplier * colorBrightness / 100; + int blueAdj = blue * multiplier * colorBrightness / 100; + int brightnessAdj = brightness * multiplier * 255 / 100; #ifdef ARDUINO_ARCH_ESP32 - ledcWrite(redPin, red); - ledcWrite(greenPin, green); - ledcWrite(bluePin, blue); - ledcWrite(colorBrightnessPin, (colorBrightness * 255) / 100); - ledcWrite(brightnessPin, (brightness * 255) / 100); + ledcWrite(redPin, redAdj); + ledcWrite(greenPin, greenAdj); + ledcWrite(bluePin, blueAdj); + ledcWrite(brightnessPin, brightnessAdj); #else - analogWrite(redPin, red); - analogWrite(greenPin, green); - analogWrite(bluePin, blue); - analogWrite(brightnessPin, (brightness * 255) / 100); - analogWrite(colorBrightnessPin, (colorBrightness * 255) / 100); + analogWrite(redPin, redAdj); + analogWrite(greenPin, greenAdj); + analogWrite(bluePin, blueAdj); + analogWrite(brightnessPin, brightnessAdj); #endif } @@ -59,29 +68,24 @@ void Supla::Control::RGBWLeds::onInit() { Serial.print(F(" to PWM channel: ")); Serial.println(esp32PwmChannelCouner); - ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcSetup(esp32PwmChannelCouner, 12000, 10); ledcAttachPin(redPin, esp32PwmChannelCouner); // on ESP32 we write to PWM channels instead of pins, so we copy channel // number as pin in order to reuse variable redPin = esp32PwmChannelCouner; esp32PwmChannelCouner++; - ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcSetup(esp32PwmChannelCouner, 12000, 10); ledcAttachPin(greenPin, esp32PwmChannelCouner); greenPin = esp32PwmChannelCouner; esp32PwmChannelCouner++; - ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcSetup(esp32PwmChannelCouner, 12000, 10); ledcAttachPin(bluePin, esp32PwmChannelCouner); bluePin = esp32PwmChannelCouner; esp32PwmChannelCouner++; - ledcSetup(esp32PwmChannelCouner, 12000, 8); - ledcAttachPin(colorBrightnessPin, esp32PwmChannelCouner); - colorBrightnessPin = esp32PwmChannelCouner; - esp32PwmChannelCouner++; - - ledcSetup(esp32PwmChannelCouner, 12000, 8); + ledcSetup(esp32PwmChannelCouner, 12000, 10); ledcAttachPin(brightnessPin, esp32PwmChannelCouner); brightnessPin = esp32PwmChannelCouner; esp32PwmChannelCouner++; @@ -90,11 +94,10 @@ void Supla::Control::RGBWLeds::onInit() { pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); - pinMode(colorBrightnessPin, OUTPUT); pinMode(brightnessPin, OUTPUT); #ifdef ARDUINO_ARCH_ESP8266 - analogWriteRange(255); + analogWriteRange(1024); #endif #endif diff --git a/src/supla/control/rgbw_leds.h b/src/supla/control/rgbw_leds.h index 62e8c402..9457afca 100644 --- a/src/supla/control/rgbw_leds.h +++ b/src/supla/control/rgbw_leds.h @@ -26,7 +26,6 @@ class RGBWLeds : public RGBWBase { RGBWLeds(int redPin, int greenPin, int bluePin, - int colorBrightnessPin, int brightnessPin); void setRGBWValueOnDevice(uint8_t red, @@ -42,7 +41,6 @@ class RGBWLeds : public RGBWBase { int greenPin; int bluePin; int brightnessPin; - int colorBrightnessPin; }; }; // namespace Control From 1d63885be26190ac286bdcbde93ccc50855b7e8e Mon Sep 17 00:00:00 2001 From: klew Date: Mon, 8 Feb 2021 15:42:47 +0100 Subject: [PATCH 12/38] Changed default fade time for RGBW to 500 ms --- src/supla/control/rgbw_base.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/supla/control/rgbw_base.cpp b/src/supla/control/rgbw_base.cpp index 70cbf258..1fc96361 100644 --- a/src/supla/control/rgbw_base.cpp +++ b/src/supla/control/rgbw_base.cpp @@ -44,7 +44,7 @@ RGBWBase::RGBWBase() defaultDimmedBrightness(20), dimIterationDirection(false), iterationDelayCounter(0), - fadeEffect(1000), + fadeEffect(500), hwRed(0), hwGreen(0), hwBlue(0), From c5936d6a7c612c9596556eaa9e1db2be3c5e74f8 Mon Sep 17 00:00:00 2001 From: klew Date: Wed, 10 Feb 2021 10:44:40 +0100 Subject: [PATCH 13/38] Added delay(0) in few places to help software WDT get some air time --- src/supla/element.cpp | 1 + src/supla/network/esp_wifi.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/supla/element.cpp b/src/supla/element.cpp index 70fc8a11..9d16c3ae 100644 --- a/src/supla/element.cpp +++ b/src/supla/element.cpp @@ -65,6 +65,7 @@ Element *Element::getElementByChannelNumber(int channelNumber) { } Element *Element::next() { + delay(0); return nextPtr; } diff --git a/src/supla/network/esp_wifi.h b/src/supla/network/esp_wifi.h index adbeac69..c02e5090 100644 --- a/src/supla/network/esp_wifi.h +++ b/src/supla/network/esp_wifi.h @@ -67,6 +67,7 @@ class ESPWifi : public Supla::Network { for (int i = 0; i < readSize; i++) { Serial.print(static_cast(buf)[i], HEX); Serial.print(F(" ")); + delay(0); } Serial.println(F("]")); #endif @@ -82,6 +83,7 @@ class ESPWifi : public Supla::Network { for (int i = 0; i < count; i++) { Serial.print(static_cast(buf)[i], HEX); Serial.print(F(" ")); + delay(0); } Serial.println(F("]")); #endif @@ -127,7 +129,7 @@ class ESPWifi : public Supla::Network { server, connectionPort); - // static_cast(client)->setBufferSizes(512, 512); // +// static_cast(client)->setBufferSizes(512, 512); // // EXPERIMENTAL bool result = client->connect(server, connectionPort); From 66ab551184132a1d0c42881d079e9a9fcf5cf339 Mon Sep 17 00:00:00 2001 From: klew Date: Wed, 10 Feb 2021 13:00:22 +0100 Subject: [PATCH 14/38] Extracted HC_SR04 to cpp --- src/supla/sensor/HC_SR04.cpp | 82 ++++++++++++++++++++++++++++++++++++ src/supla/sensor/HC_SR04.h | 70 ++++++------------------------ 2 files changed, 96 insertions(+), 56 deletions(-) create mode 100644 src/supla/sensor/HC_SR04.cpp diff --git a/src/supla/sensor/HC_SR04.cpp b/src/supla/sensor/HC_SR04.cpp new file mode 100644 index 00000000..a1c0a63b --- /dev/null +++ b/src/supla/sensor/HC_SR04.cpp @@ -0,0 +1,82 @@ +/* + 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 + +#include "HC_SR04.h" + +namespace Supla { +namespace Sensor { +HC_SR04::HC_SR04(int8_t trigPin, + int8_t echoPin, + int16_t minIn, + int16_t maxIn, + int16_t minOut, + int16_t maxOut) + : failCount(0), lastDuration(0) { + _trigPin = trigPin; + _echoPin = echoPin; + _minIn = minIn; + _maxIn = maxIn; + _minOut = minOut; + _maxOut = maxOut; +} + +void HC_SR04::onInit() { + pinMode(_trigPin, OUTPUT); + pinMode(_echoPin, INPUT); + digitalWrite(_trigPin, LOW); + delayMicroseconds(2); + + channel.setNewValue(getValue()); + channel.setNewValue(getValue()); +} + +double HC_SR04::getValue() { + digitalWrite(_trigPin, HIGH); + delayMicroseconds(10); + digitalWrite(_trigPin, LOW); + unsigned long duration = pulseIn(_echoPin, HIGH, 60000); + if (duration > 50) { + lastDuration = duration; + failCount = 0; + } else { + duration = lastDuration; + failCount++; + } + + long distance = (duration / 2.0) / 29.1; + long value = map(distance, _minIn, _maxIn, _minOut, _maxOut); + if (_minOut < _maxOut) { + value = constrain(value, _minOut, _maxOut); + } else { + value = constrain(value, _maxOut, _minOut); + } + return failCount <= 3 ? (float)value / 100.0 : DISTANCE_NOT_AVAILABLE; +} + +void HC_SR04::setMinMaxIn(int16_t minIn, int16_t maxIn) { + _minIn = minIn; + _maxIn = maxIn; +} + +void HC_SR04::setMinMaxOut(int16_t minOut, int16_t maxOut) { + _minOut = minOut; + _maxOut = maxOut; +} + +}; // namespace Sensor +}; // namespace Supla diff --git a/src/supla/sensor/HC_SR04.h b/src/supla/sensor/HC_SR04.h index ecd9ea1f..1c5374e5 100644 --- a/src/supla/sensor/HC_SR04.h +++ b/src/supla/sensor/HC_SR04.h @@ -26,60 +26,18 @@ namespace Supla { namespace Sensor { class HC_SR04 : public Distance { public: - HC_SR04(int8_t trigPin, int8_t echoPin, int16_t minIn = 0, - int16_t maxIn = 500, int16_t minOut = 0, int16_t maxOut = 500) - : failCount(0), lastDuration(0) { - _trigPin = trigPin; - _echoPin = echoPin; - _minIn = minIn; - _maxIn = maxIn; - _minOut = minOut; - _maxOut = maxOut; - } - void onInit() { - pinMode(_trigPin, OUTPUT); - pinMode(_echoPin, INPUT); - digitalWrite(_trigPin, LOW); - delayMicroseconds(2); - - channel.setNewValue(getValue()); - channel.setNewValue(getValue()); - } - - virtual double getValue() { - digitalWrite(_trigPin, HIGH); - delayMicroseconds(10); - digitalWrite(_trigPin, LOW); - unsigned long duration = pulseIn(_echoPin, HIGH, 60000); - if (duration > 50) { - lastDuration = duration; - failCount = 0; - } else { - duration = lastDuration; - failCount++; - } - - long distance = (duration / 2.0) / 29.1; - long value = map(distance, _minIn, _maxIn, _minOut, _maxOut); - if (_minOut < _maxOut) { - value = constrain(value, _minOut, _maxOut); - } else { - value = constrain(value, _maxOut, _minOut); - } - return failCount <= 3 ? (float)value / 100.0 : DISTANCE_NOT_AVAILABLE; - } - - void setMinMaxIn(int16_t minIn, int16_t maxIn) { - _minIn = minIn; - _maxIn = maxIn; - } - - void setMinMaxOut(int16_t minOut, int16_t maxOut) { - _minOut = minOut; - _maxOut = maxOut; - } - -protected: + HC_SR04(int8_t trigPin, + int8_t echoPin, + int16_t minIn = 0, + int16_t maxIn = 500, + int16_t minOut = 0, + int16_t maxOut = 500); + void onInit(); + virtual double getValue(); + void setMinMaxIn(int16_t minIn, int16_t maxIn); + void setMinMaxOut(int16_t minOut, int16_t maxOut); + + protected: int8_t _trigPin; int8_t _echoPin; int16_t _minIn; @@ -91,7 +49,7 @@ class HC_SR04 : public Distance { unsigned long lastDuration; }; -}; // namespace Sensor -}; // namespace Supla +}; // namespace Sensor +}; // namespace Supla #endif From 7fa81014641799ac880a0e7cd8e444fc3da15a8f Mon Sep 17 00:00:00 2001 From: klew Date: Thu, 11 Feb 2021 13:13:40 +0100 Subject: [PATCH 15/38] RGBW PWM scalling in range to 1023 --- .../RgbwDimmerTests/dimmer_leds_tests.cpp | 4 +- extras/test/RgbwDimmerTests/dimmer_tests.cpp | 6 +- .../test/RgbwDimmerTests/rgb_base_tests.cpp | 8 +- .../test/RgbwDimmerTests/rgb_leds_tests.cpp | 22 ++-- .../test/RgbwDimmerTests/rgbw_base_tests.cpp | 59 +++++----- .../test/RgbwDimmerTests/rgbw_leds_test.cpp | 10 +- extras/test/doubles/Arduino.h | 3 +- extras/test/doubles/arduino_mock.cpp | 6 + src/supla/control/dimmer_leds.cpp | 21 ++-- src/supla/control/dimmer_leds.h | 10 +- src/supla/control/rgb_leds.cpp | 49 ++++---- src/supla/control/rgb_leds.h | 10 +- src/supla/control/rgbw_base.cpp | 110 ++++++++---------- src/supla/control/rgbw_base.h | 10 +- src/supla/control/rgbw_leds.cpp | 30 ++--- src/supla/control/rgbw_leds.h | 10 +- 16 files changed, 187 insertions(+), 181 deletions(-) diff --git a/extras/test/RgbwDimmerTests/dimmer_leds_tests.cpp b/extras/test/RgbwDimmerTests/dimmer_leds_tests.cpp index f0b41062..a4ef9c1a 100644 --- a/extras/test/RgbwDimmerTests/dimmer_leds_tests.cpp +++ b/extras/test/RgbwDimmerTests/dimmer_leds_tests.cpp @@ -37,7 +37,7 @@ TEST(DimmerLedsTests, SettingNewDimValue) { EXPECT_CALL(ioMock, pinMode(1, OUTPUT)); EXPECT_CALL(ioMock, analogWrite(1, 0)); - EXPECT_CALL(ioMock, analogWrite(1, (255*10)/100)); + EXPECT_CALL(ioMock, analogWrite(1, (1023*10)/100)); Supla::Control::DimmerLeds dim(1); @@ -60,6 +60,7 @@ TEST(DimmerLedsTests, SettingNewDimValue) { EXPECT_EQ(ch->getValueBrightness(), 0); dim.iterateAlways(); + dim.onTimer(); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 0); @@ -70,6 +71,7 @@ TEST(DimmerLedsTests, SettingNewDimValue) { dim.setRGBW(1, 2, 3, 4, 10); dim.iterateAlways(); + dim.onTimer(); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 0); diff --git a/extras/test/RgbwDimmerTests/dimmer_tests.cpp b/extras/test/RgbwDimmerTests/dimmer_tests.cpp index db80823e..00086259 100644 --- a/extras/test/RgbwDimmerTests/dimmer_tests.cpp +++ b/extras/test/RgbwDimmerTests/dimmer_tests.cpp @@ -25,7 +25,7 @@ using ::testing::Return; class DimmerBaseForTest : public Supla::Control::DimmerBase { public: - MOCK_METHOD(void, setRGBWValueOnDevice, (uint8_t, uint8_t, uint8_t, uint8_t, uint8_t), (override)); + MOCK_METHOD(void, setRGBWValueOnDevice, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), (override)); }; class TimeInterfaceStub : public TimeInterface { @@ -80,7 +80,7 @@ TEST(DimmerTests, DimmerShouldIgnoreRGBValues) { dimmer.setFadeEffectTime(0); EXPECT_CALL(dimmer, setRGBWValueOnDevice(0, 0, 0, 0, 0)).Times(1); - EXPECT_CALL(dimmer, setRGBWValueOnDevice(0, 0, 0, 0, 5)).Times(1); + EXPECT_CALL(dimmer, setRGBWValueOnDevice(0, 0, 0, 0, (5*1023/100))).Times(1); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 0); @@ -97,6 +97,7 @@ TEST(DimmerTests, DimmerShouldIgnoreRGBValues) { EXPECT_EQ(ch->getValueBrightness(), 0); dimmer.iterateAlways(); + dimmer.onTimer(); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 0); @@ -108,6 +109,7 @@ TEST(DimmerTests, DimmerShouldIgnoreRGBValues) { dimmer.setRGBW(1, 2, 3, 4, 5); dimmer.iterateAlways(); + dimmer.onTimer(); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 0); diff --git a/extras/test/RgbwDimmerTests/rgb_base_tests.cpp b/extras/test/RgbwDimmerTests/rgb_base_tests.cpp index 974a70fa..a9447222 100644 --- a/extras/test/RgbwDimmerTests/rgb_base_tests.cpp +++ b/extras/test/RgbwDimmerTests/rgb_base_tests.cpp @@ -25,7 +25,7 @@ using ::testing::Return; class RgbBaseForTest : public Supla::Control::RGBBase { public: - MOCK_METHOD(void, setRGBWValueOnDevice, (uint8_t, uint8_t, uint8_t, uint8_t, uint8_t), (override)); + MOCK_METHOD(void, setRGBWValueOnDevice, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), (override)); }; class TimeInterfaceStub : public TimeInterface { @@ -79,8 +79,8 @@ TEST(RgbTests, RgbShouldIgnoreBrightnessValue) { // disable fading effect so we'll get instant setting value on device call rgb.setFadeEffectTime(0); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)).Times(1); - EXPECT_CALL(rgb, setRGBWValueOnDevice(1, 2, 3, 4, 0)).Times(1); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 0, 0)).Times(1); + EXPECT_CALL(rgb, setRGBWValueOnDevice((1*1023/255), (2*1023/255), (3*1023/255), (4*1023/100), 0)).Times(1); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 0); @@ -97,6 +97,7 @@ TEST(RgbTests, RgbShouldIgnoreBrightnessValue) { EXPECT_EQ(ch->getValueBrightness(), 0); rgb.iterateAlways(); + rgb.onTimer(); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 255); @@ -108,6 +109,7 @@ TEST(RgbTests, RgbShouldIgnoreBrightnessValue) { rgb.setRGBW(1, 2, 3, 4, 5); rgb.iterateAlways(); + rgb.onTimer(); EXPECT_EQ(ch->getValueRed(), 1); EXPECT_EQ(ch->getValueGreen(), 2); diff --git a/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp b/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp index 9c0e6c08..a2861b20 100644 --- a/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp +++ b/extras/test/RgbwDimmerTests/rgb_leds_tests.cpp @@ -41,15 +41,15 @@ TEST(RgbLedsTests, SettingNewRGBValue) { EXPECT_CALL(ioMock, analogWrite(2, 0)); EXPECT_CALL(ioMock, analogWrite(3, 0)); - EXPECT_CALL(ioMock, analogWrite(1, 1)); - EXPECT_CALL(ioMock, analogWrite(2, 2)); - EXPECT_CALL(ioMock, analogWrite(3, 3)); + EXPECT_CALL(ioMock, analogWrite(1, (1*1023/255))); + EXPECT_CALL(ioMock, analogWrite(2, (2*1023/255))); + EXPECT_CALL(ioMock, analogWrite(3, (3*1023/255))); - Supla::Control::RGBLeds rgbw(1, 2, 3); + Supla::Control::RGBLeds rgb(1, 2, 3); - auto ch = rgbw.getChannel(); + auto ch = rgb.getChannel(); // disable fading effect so we'll get instant setting value on device call - rgbw.setFadeEffectTime(0); + rgb.setFadeEffectTime(0); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 0); @@ -57,7 +57,8 @@ TEST(RgbLedsTests, SettingNewRGBValue) { EXPECT_EQ(ch->getValueColorBrightness(), 0); EXPECT_EQ(ch->getValueBrightness(), 0); - rgbw.onInit(); + rgb.onInit(); + rgb.onTimer(); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 0); @@ -65,7 +66,7 @@ TEST(RgbLedsTests, SettingNewRGBValue) { EXPECT_EQ(ch->getValueColorBrightness(), 0); EXPECT_EQ(ch->getValueBrightness(), 0); - rgbw.iterateAlways(); + rgb.iterateAlways(); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 255); @@ -73,9 +74,10 @@ TEST(RgbLedsTests, SettingNewRGBValue) { EXPECT_EQ(ch->getValueColorBrightness(), 0); EXPECT_EQ(ch->getValueBrightness(), 0); - rgbw.setRGBW(1, 2, 3, 100, 5); + rgb.setRGBW(1, 2, 3, 100, 5); - rgbw.iterateAlways(); + rgb.iterateAlways(); + rgb.onTimer(); EXPECT_EQ(ch->getValueRed(), 1); EXPECT_EQ(ch->getValueGreen(), 2); diff --git a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp index 6cccba01..de0c97a1 100644 --- a/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp +++ b/extras/test/RgbwDimmerTests/rgbw_base_tests.cpp @@ -25,7 +25,7 @@ using ::testing::Return; class RgbwBaseForTest : public Supla::Control::RGBWBase { public: - MOCK_METHOD(void, setRGBWValueOnDevice, (uint8_t, uint8_t, uint8_t, uint8_t, uint8_t), (override)); + MOCK_METHOD(void, setRGBWValueOnDevice, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), (override)); }; class TimeInterfaceStub : public TimeInterface { @@ -943,12 +943,12 @@ TEST(RgbwDimmerTests, SetValueOnDeviceWithoutFading) { ::testing::InSequence seq; RgbwBaseForTest rgb; - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 100, 100)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 100, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 1023, 1023)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 0, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 1023, (20*1023/100))); auto ch = rgb.getChannel(); @@ -956,11 +956,17 @@ TEST(RgbwDimmerTests, SetValueOnDeviceWithoutFading) { // disable fade effect - so value setting on device should happen instantly rgb.setFadeEffectTime(0); rgb.onInit(); + rgb.onTimer(); rgb.turnOn(); + rgb.onTimer(); rgb.toggle(); + rgb.onTimer(); rgb.handleAction(1, Supla::TURN_ON_W_DIMMED); + rgb.onTimer(); rgb.turnOff(); + rgb.onTimer(); rgb.turnOn(); + rgb.onTimer(); // channel value should be still empty, since no time elapsed (no calls to iterateAlways) EXPECT_EQ(ch->getValueRed(), 0); @@ -978,33 +984,34 @@ TEST(RgbwDimmerTests, SetValueOnDeviceWithFading) { RgbwBaseForTest rgb; // fade effect 1000 ms, time step 1000 ms - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 100, 100)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 100, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 1023, 1023)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 0, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 0, 0)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 1023, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 0, 0)); // fade effect 10000 ms, time step 1000 ms // because turnOn calls millis once, we actually do 2 s step in first shot // that is why (0, 255, 0, 10, 10) is missing - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 20, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 30, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 40, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 50, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 60, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 70, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 80, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 90, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 255, 0, 100, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 204, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 306, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 408, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 510, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 612, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 714, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 816, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 918, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 1020, (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(0, 1023, 0, 1023, (20*1023/100))); // fade effect 10000 ms, time step 1000 ms // because turnOn calls millis once, we actually do 2 s step in first shot // setting rgb values - EXPECT_CALL(rgb, setRGBWValueOnDevice(51, 204, 0, 100, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(76, 200, 0, 100, 20)); - EXPECT_CALL(rgb, setRGBWValueOnDevice(100, 200, 0, 100, 20)); + EXPECT_CALL(rgb, setRGBWValueOnDevice(204, 818, 0, (100*1023/100), (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(306, 802, 0, (100*1023/100), (20*1023/100))); + EXPECT_CALL(rgb, setRGBWValueOnDevice(401, (1023*200/255), 0, (100*1023/100), (20*1023/100))); auto ch = rgb.getChannel(); diff --git a/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp b/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp index 02e76374..af398445 100644 --- a/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp +++ b/extras/test/RgbwDimmerTests/rgbw_leds_test.cpp @@ -43,10 +43,10 @@ TEST(RgbwLedsTests, SettingNewRGBWValue) { EXPECT_CALL(ioMock, analogWrite(3, 0)); EXPECT_CALL(ioMock, analogWrite(4, 0)); - EXPECT_CALL(ioMock, analogWrite(1, 1)); - EXPECT_CALL(ioMock, analogWrite(2, 2)); - EXPECT_CALL(ioMock, analogWrite(3, 3)); - EXPECT_CALL(ioMock, analogWrite(4, 255)); + EXPECT_CALL(ioMock, analogWrite(1, (1*1023/255))); + EXPECT_CALL(ioMock, analogWrite(2, (2*1023/255))); + EXPECT_CALL(ioMock, analogWrite(3, (3*1023/255))); + EXPECT_CALL(ioMock, analogWrite(4, 1023)); Supla::Control::RGBWLeds rgbw(1, 2, 3, 4); @@ -69,6 +69,7 @@ TEST(RgbwLedsTests, SettingNewRGBWValue) { EXPECT_EQ(ch->getValueBrightness(), 0); rgbw.iterateAlways(); + rgbw.onTimer(); EXPECT_EQ(ch->getValueRed(), 0); EXPECT_EQ(ch->getValueGreen(), 255); @@ -79,6 +80,7 @@ TEST(RgbwLedsTests, SettingNewRGBWValue) { rgbw.setRGBW(1, 2, 3, 100, 100); rgbw.iterateAlways(); + rgbw.onTimer(); EXPECT_EQ(ch->getValueRed(), 1); EXPECT_EQ(ch->getValueGreen(), 2); diff --git a/extras/test/doubles/Arduino.h b/extras/test/doubles/Arduino.h index 066c9729..b3fe6072 100644 --- a/extras/test/doubles/Arduino.h +++ b/extras/test/doubles/Arduino.h @@ -22,7 +22,8 @@ int digitalRead(uint8_t pin); void analogWrite(uint8_t pin, int val); void pinMode(uint8_t pin, uint8_t mode); unsigned long millis(); - +void delay(unsigned long ms); +long map(long, long, long, long, long); class SerialStub { public: diff --git a/extras/test/doubles/arduino_mock.cpp b/extras/test/doubles/arduino_mock.cpp index 981447ac..a44b6e67 100644 --- a/extras/test/doubles/arduino_mock.cpp +++ b/extras/test/doubles/arduino_mock.cpp @@ -61,4 +61,10 @@ unsigned long millis() { return TimeInterface::instance->millis(); } +void delay(unsigned long ms) {}; + +long map(long input, long inMin, long inMax, long outMin, long outMax) { + long result = (input - inMin) * (outMax - outMin) / (inMax - inMin); + return result + outMin; +} diff --git a/src/supla/control/dimmer_leds.cpp b/src/supla/control/dimmer_leds.cpp index 13781eb1..10abcb05 100644 --- a/src/supla/control/dimmer_leds.cpp +++ b/src/supla/control/dimmer_leds.cpp @@ -24,20 +24,17 @@ Supla::Control::DimmerLeds::DimmerLeds(int brightnessPin) : brightnessPin(brightnessPin) { } -void Supla::Control::DimmerLeds::setRGBWValueOnDevice(uint8_t red, - uint8_t green, - uint8_t blue, - uint8_t colorBrightness, - uint8_t brightness) { - int multiplier = 1; -#ifdef ARDUINO_ARCH_ESP8266 - multiplier = 4; -#endif -#ifdef ARDUINO_ARCH_ESP32 - multiplier = 4; +void Supla::Control::DimmerLeds::setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness) { + uint32_t brightnessAdj = brightness; + +#ifdef ARDUINO_ARCH_AVR + brightnessAdj = map(brightnessAdj, 0, 1023, 0, 255); #endif - int brightnessAdj = brightness * multiplier * 255 / 100; #ifdef ARDUINO_ARCH_ESP32 ledcWrite(brightnessPin, brightnessAdj); #else diff --git a/src/supla/control/dimmer_leds.h b/src/supla/control/dimmer_leds.h index 7fed3d48..041639e5 100644 --- a/src/supla/control/dimmer_leds.h +++ b/src/supla/control/dimmer_leds.h @@ -25,11 +25,11 @@ class DimmerLeds : public DimmerBase { public: DimmerLeds(int brightnessPin); - void setRGBWValueOnDevice(uint8_t red, - uint8_t green, - uint8_t blue, - uint8_t colorBrightness, - uint8_t brightness); + void setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness); void onInit(); diff --git a/src/supla/control/rgb_leds.cpp b/src/supla/control/rgb_leds.cpp index b0ff0979..1efc29dd 100644 --- a/src/supla/control/rgb_leds.cpp +++ b/src/supla/control/rgb_leds.cpp @@ -20,30 +20,24 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern int esp32PwmChannelCouner; #endif -Supla::Control::RGBLeds::RGBLeds(int redPin, - int greenPin, - int bluePin) - : redPin(redPin), - greenPin(greenPin), - bluePin(bluePin) -{} +Supla::Control::RGBLeds::RGBLeds(int redPin, int greenPin, int bluePin) + : redPin(redPin), greenPin(greenPin), bluePin(bluePin) { +} -void Supla::Control::RGBLeds::setRGBWValueOnDevice(uint8_t red, - uint8_t green, - uint8_t blue, - uint8_t colorBrightness, - uint8_t brightness) { - int multiplier = 1; -#ifdef ARDUINO_ARCH_ESP8266 - multiplier = 4; -#endif -#ifdef ARDUINO_ARCH_ESP32 - multiplier = 4; -#endif +void Supla::Control::RGBLeds::setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness) { + uint32_t redAdj = red * colorBrightness / 1023; + uint32_t greenAdj = green * colorBrightness / 1023; + uint32_t blueAdj = blue * colorBrightness / 1023; - int redAdj = red * multiplier * colorBrightness / 100; - int greenAdj = green* multiplier * colorBrightness / 100; - int blueAdj = blue * multiplier * colorBrightness / 100; +#ifdef ARDUINO_ARCH_AVR + redAdj = map(redAdj, 0, 1023, 0, 255); + greenAdj = map(greenAdj, 0, 1023, 0, 255); + blueAdj = map(blueAdj, 0, 1023, 0, 255); +#endif #ifdef ARDUINO_ARCH_ESP32 ledcWrite(redPin, redAdj); @@ -81,15 +75,14 @@ void Supla::Control::RGBLeds::onInit() { esp32PwmChannelCouner++; #else - pinMode(redPin, OUTPUT); - pinMode(greenPin, OUTPUT); - pinMode(bluePin, OUTPUT); + pinMode(redPin, OUTPUT); + pinMode(greenPin, OUTPUT); + pinMode(bluePin, OUTPUT); - #ifdef ARDUINO_ARCH_ESP8266 +#ifdef ARDUINO_ARCH_ESP8266 analogWriteRange(1024); - #endif +#endif #endif Supla::Control::RGBBase::onInit(); } - diff --git a/src/supla/control/rgb_leds.h b/src/supla/control/rgb_leds.h index 372052c5..bebddd3c 100644 --- a/src/supla/control/rgb_leds.h +++ b/src/supla/control/rgb_leds.h @@ -25,11 +25,11 @@ class RGBLeds : public RGBBase { public: RGBLeds(int redPin, int greenPin, int bluePin); - void setRGBWValueOnDevice(uint8_t red, - uint8_t green, - uint8_t blue, - uint8_t colorBrightness, - uint8_t brightness); + void setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness); void onInit(); diff --git a/src/supla/control/rgbw_base.cpp b/src/supla/control/rgbw_base.cpp index 1fc96361..228b5a44 100644 --- a/src/supla/control/rgbw_base.cpp +++ b/src/supla/control/rgbw_base.cpp @@ -45,7 +45,7 @@ RGBWBase::RGBWBase() dimIterationDirection(false), iterationDelayCounter(0), fadeEffect(500), - hwRed(0), + hwRed(-1), hwGreen(0), hwBlue(0), hwColorBrightness(0), @@ -98,12 +98,6 @@ void RGBWBase::setRGBW(int red, curBrightness = brightness; } - // If fade effect is disabled, then set new values to device directly - if (fadeEffect <= 0) { - setRGBWValueOnDevice( - curRed, curGreen, curBlue, curColorBrightness, curBrightness); - } - // Schedule save in 5 s after state change Supla::Storage::ScheduleSave(5000); } @@ -355,96 +349,94 @@ void RGBWBase::setFadeEffectTime(int timeMs) { } void RGBWBase::onTimer() { - // exit it fade effect is disabled - if (fadeEffect <= 0) { - return; - } unsigned long timeDiff = millis() - lastTick; lastTick = millis(); if (timeDiff > 0) { - int divider = fadeEffect / timeDiff; + double divider = 1.0* fadeEffect / timeDiff; if (divider <= 0) { divider = 1; } - uint8_t rgbStep = 255 / divider; - uint8_t brightnessStep = 100 / divider; + double step = 1023 / divider; bool valueChanged = false; - if (rgbStep < 1) { - rgbStep = 1; - } - if (brightnessStep < 1) { - brightnessStep = 1; + if (step < 1) { + step = 1; } - if (curRed > hwRed) { + int curRedAdj = map(curRed, 0, 255, 0, 1023); + int curGreenAdj = map(curGreen, 0, 255, 0 , 1023); + int curBlueAdj = map(curBlue, 0, 255, 0, 1023); + int curColorBrightnessAdj = map(curColorBrightness, 0, 100, 0, 1023); + int curBrightnessAdj = map(curBrightness, 0, 100, 0, 1023); + + if (curRedAdj > hwRed) { valueChanged = true; - hwRed += rgbStep; - if (hwRed > curRed) { - hwRed = curRed; + hwRed += step; + if (hwRed > curRedAdj) { + hwRed = curRedAdj; } - } else if (curRed < hwRed) { + } else if (curRedAdj < hwRed) { valueChanged = true; - hwRed -= rgbStep; - if (hwRed < curRed) { - hwRed = curRed; + hwRed -= step; + if (hwRed < curRedAdj) { + hwRed = curRedAdj; } } - if (curGreen > hwGreen) { + if (curGreenAdj > hwGreen) { valueChanged = true; - hwGreen += rgbStep; - if (hwGreen > curGreen) { - hwGreen = curGreen; + hwGreen += step; + if (hwGreen > curGreenAdj) { + hwGreen = curGreenAdj; } - } else if (curGreen < hwGreen) { + } else if (curGreenAdj < hwGreen) { valueChanged = true; - hwGreen -= rgbStep; - if (hwGreen < curGreen) { - hwGreen = curGreen; + hwGreen -= step; + if (hwGreen < curGreenAdj) { + hwGreen = curGreenAdj; } } - if (curBlue > hwBlue) { + if (curBlueAdj > hwBlue) { valueChanged = true; - hwBlue += rgbStep; - if (hwBlue > curBlue) { - hwBlue = curBlue; + hwBlue += step; + if (hwBlue > curBlueAdj) { + hwBlue = curBlueAdj; } - } else if (curBlue < hwBlue) { + } else if (curBlueAdj < hwBlue) { valueChanged = true; - hwBlue -= rgbStep; - if (hwBlue < curBlue) { - hwBlue = curBlue; + hwBlue -= step; + if (hwBlue < curBlueAdj) { + hwBlue = curBlueAdj; } } - if (curColorBrightness > hwColorBrightness) { + if (curColorBrightnessAdj > hwColorBrightness) { valueChanged = true; - hwColorBrightness += brightnessStep; - if (hwColorBrightness > curColorBrightness) { - hwColorBrightness = curColorBrightness; + hwColorBrightness += step; + if (hwColorBrightness > curColorBrightnessAdj) { + hwColorBrightness = curColorBrightnessAdj; } - } else if (curColorBrightness < hwColorBrightness) { + } else if (curColorBrightnessAdj < hwColorBrightness) { valueChanged = true; - hwColorBrightness -= brightnessStep; - if (hwColorBrightness < curColorBrightness) { - hwColorBrightness = curColorBrightness; + hwColorBrightness -= step; + if (hwColorBrightness < curColorBrightnessAdj) { + hwColorBrightness = curColorBrightnessAdj; } } - if (curBrightness > hwBrightness) { + if (curBrightnessAdj > hwBrightness) { valueChanged = true; - hwBrightness += brightnessStep; - if (hwBrightness > curBrightness) { - hwBrightness = curBrightness; + hwBrightness += step; + if (hwBrightness > curBrightnessAdj) { + hwBrightness = curBrightnessAdj; } - } else if (curBrightness < hwBrightness) { + } else if (curBrightnessAdj < hwBrightness) { valueChanged = true; - hwBrightness -= brightnessStep; - if (hwBrightness < curBrightness) { - hwBrightness = curBrightness; + hwBrightness -= step; + if (hwBrightness < curBrightnessAdj) { + hwBrightness = curBrightnessAdj; } } diff --git a/src/supla/control/rgbw_base.h b/src/supla/control/rgbw_base.h index 5f3790fd..c6d3ac41 100644 --- a/src/supla/control/rgbw_base.h +++ b/src/supla/control/rgbw_base.h @@ -30,11 +30,11 @@ class RGBWBase : public ChannelElement, public ActionHandler { public: RGBWBase(); - virtual void setRGBWValueOnDevice(uint8_t red, - uint8_t green, - uint8_t blue, - uint8_t colorBrightness, - uint8_t brightness) = 0; + virtual void setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness) = 0; virtual void setRGBW(int red, int green, diff --git a/src/supla/control/rgbw_leds.cpp b/src/supla/control/rgbw_leds.cpp index 1e11a112..11f85f9d 100644 --- a/src/supla/control/rgbw_leds.cpp +++ b/src/supla/control/rgbw_leds.cpp @@ -31,23 +31,23 @@ Supla::Control::RGBWLeds::RGBWLeds(int redPin, brightnessPin(brightnessPin) { } -void Supla::Control::RGBWLeds::setRGBWValueOnDevice(uint8_t red, - uint8_t green, - uint8_t blue, - uint8_t colorBrightness, - uint8_t brightness) { - int multiplier = 1; -#ifdef ARDUINO_ARCH_ESP8266 - multiplier = 4; -#endif -#ifdef ARDUINO_ARCH_ESP32 - multiplier = 4; +void Supla::Control::RGBWLeds::setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness) { + uint32_t redAdj = red * colorBrightness / 1023; + uint32_t greenAdj = green * colorBrightness / 1023; + uint32_t blueAdj = blue * colorBrightness / 1023; + uint32_t brightnessAdj = brightness; + +#ifdef ARDUINO_ARCH_AVR + redAdj = map(redAdj, 0, 1023, 0, 255); + greenAdj = map(greenAdj, 0, 1023, 0, 255); + blueAdj = map(blueAdj, 0, 1023, 0, 255); + brightnessAdj = map(brightnessAdj, 0, 1023, 0, 255); #endif - int redAdj = red * multiplier * colorBrightness / 100; - int greenAdj = green* multiplier * colorBrightness / 100; - int blueAdj = blue * multiplier * colorBrightness / 100; - int brightnessAdj = brightness * multiplier * 255 / 100; #ifdef ARDUINO_ARCH_ESP32 ledcWrite(redPin, redAdj); ledcWrite(greenPin, greenAdj); diff --git a/src/supla/control/rgbw_leds.h b/src/supla/control/rgbw_leds.h index 9457afca..b3133e99 100644 --- a/src/supla/control/rgbw_leds.h +++ b/src/supla/control/rgbw_leds.h @@ -28,11 +28,11 @@ class RGBWLeds : public RGBWBase { int bluePin, int brightnessPin); - void setRGBWValueOnDevice(uint8_t red, - uint8_t green, - uint8_t blue, - uint8_t colorBrightness, - uint8_t brightness); + void setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness); void onInit(); From c293e8f1e4b81769fa7c91fc9766d640fe3cba1e Mon Sep 17 00:00:00 2001 From: klew Date: Thu, 11 Feb 2021 14:07:13 +0100 Subject: [PATCH 16/38] Added averaging for HC_SR04 sensor --- src/supla/sensor/HC_SR04.cpp | 37 ++++++++++++++++++++++++++++++++---- src/supla/sensor/HC_SR04.h | 4 ++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/supla/sensor/HC_SR04.cpp b/src/supla/sensor/HC_SR04.cpp index a1c0a63b..4639556c 100644 --- a/src/supla/sensor/HC_SR04.cpp +++ b/src/supla/sensor/HC_SR04.cpp @@ -26,7 +26,7 @@ HC_SR04::HC_SR04(int8_t trigPin, int16_t maxIn, int16_t minOut, int16_t maxOut) - : failCount(0), lastDuration(0) { + : failCount(0), readouts{}, index(0) { _trigPin = trigPin; _echoPin = echoPin; _minIn = minIn; @@ -46,18 +46,46 @@ void HC_SR04::onInit() { } double HC_SR04::getValue() { + noInterrupts(); digitalWrite(_trigPin, HIGH); delayMicroseconds(10); digitalWrite(_trigPin, LOW); unsigned long duration = pulseIn(_echoPin, HIGH, 60000); + interrupts(); if (duration > 50) { - lastDuration = duration; + index++; + if (index > 4) index = 0; + readouts[index] = duration; failCount = 0; } else { - duration = lastDuration; failCount++; } + unsigned long min = 0, max = 0, sum = 0; + int count = 0; + for (int i = 0; i < 5; i++) { + if (readouts[i] > 0) { + count++; + if (min > readouts[i] || min == 0) min = readouts[i]; + if (max < readouts[i]) max = readouts[i]; + sum += readouts[i]; + } + } + + if (count == 5) { + if (min > 0) { + sum -= min; + count--; + } + if (max > 0) { + sum -= max; + count--; + } + } + if (count > 0) { + duration = sum / count; + } + long distance = (duration / 2.0) / 29.1; long value = map(distance, _minIn, _maxIn, _minOut, _maxOut); if (_minOut < _maxOut) { @@ -65,7 +93,8 @@ double HC_SR04::getValue() { } else { value = constrain(value, _maxOut, _minOut); } - return failCount <= 3 ? (float)value / 100.0 : DISTANCE_NOT_AVAILABLE; + return failCount <= 3 ? static_cast(value) / 100.0 + : DISTANCE_NOT_AVAILABLE; } void HC_SR04::setMinMaxIn(int16_t minIn, int16_t maxIn) { diff --git a/src/supla/sensor/HC_SR04.h b/src/supla/sensor/HC_SR04.h index 1c5374e5..2f222ee8 100644 --- a/src/supla/sensor/HC_SR04.h +++ b/src/supla/sensor/HC_SR04.h @@ -45,8 +45,8 @@ class HC_SR04 : public Distance { int16_t _minOut; int16_t _maxOut; char failCount; - bool ready; - unsigned long lastDuration; + unsigned long readouts[5]; + int index; }; }; // namespace Sensor From e43c70fa9e8d1786ab9d29e4dd322b14cdc1a956 Mon Sep 17 00:00:00 2001 From: klew Date: Thu, 11 Feb 2021 23:02:13 +0100 Subject: [PATCH 17/38] Simple button tests --- .../test/ButtonTests/simple_button_tests.cpp | 86 +++++++++++++++++++ extras/test/CMakeLists.txt | 1 + src/CMakeLists.txt | 1 + src/supla/control/simple_button.cpp | 9 +- 4 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 extras/test/ButtonTests/simple_button_tests.cpp diff --git a/extras/test/ButtonTests/simple_button_tests.cpp b/extras/test/ButtonTests/simple_button_tests.cpp new file mode 100644 index 00000000..59861677 --- /dev/null +++ b/extras/test/ButtonTests/simple_button_tests.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 +#include +#include +#include + +using ::testing::Return; + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + +class TimeInterfaceStub : public TimeInterface { + public: + virtual unsigned long millis() override { + static unsigned long value = 0; + value += 1000; + return value; + } +}; + +TEST(SimpleButtonTests, NoPullupInit) { + TimeInterfaceStub time; + DigitalInterfaceMock ioMock; + ActionHandlerMock mock1; + + EXPECT_CALL(ioMock, pinMode(5, INPUT)); + EXPECT_CALL(ioMock, digitalRead(5)).WillOnce(Return(0)); + + EXPECT_CALL(mock1, handleAction).Times(0); + + Supla::Control::SimpleButton button(5, false, false); + button.onInit(); + button.addAction(1, mock1, Supla::ON_PRESS); + button.addAction(2, mock1, Supla::ON_CHANGE); + button.addAction(3, mock1, Supla::ON_RELEASE); +} + +TEST(SimpleButtonTests, PullupInitAndPress) { + TimeInterfaceMock time; + DigitalInterfaceMock ioMock; + ActionHandlerMock mock1; + + EXPECT_CALL(ioMock, pinMode(5, INPUT_PULLUP)); + EXPECT_CALL(ioMock, digitalRead(5)) + .WillOnce(Return(1)) + .WillOnce(Return(0)) + .WillOnce(Return(0)) + ; + + EXPECT_CALL(time, millis) + .WillOnce(Return(1000)) + .WillOnce(Return(1100)) + ; + + + EXPECT_CALL(mock1, handleAction(Supla::ON_PRESS, 1)).Times(1); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, 2)).Times(1); + + Supla::Control::SimpleButton button(5, true, true); + button.onInit(); + button.addAction(1, mock1, Supla::ON_PRESS); + button.addAction(2, mock1, Supla::ON_CHANGE); + button.addAction(3, mock1, Supla::ON_RELEASE); + + button.onTimer(); + button.onTimer(); + +} + diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index a5f3543c..e2d48aa9 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -44,6 +44,7 @@ file(GLOB TEST_SRC PinStatusLedTests/*.cpp ConditionTests/*.cpp RgbwDimmerTests/*.cpp + ButtonTests/*.cpp ) file(GLOB DOUBLE_SRC doubles/*.cpp) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fbfd6466..55931cc9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ set(SRCS supla/control/rgbw_leds.cpp supla/control/rgb_leds.cpp supla/control/dimmer_leds.cpp + supla/control/simple_button.cpp supla/condition.cpp supla/conditions/on_less.cpp diff --git a/src/supla/control/simple_button.cpp b/src/supla/control/simple_button.cpp index 7714f675..22ae4ca8 100644 --- a/src/supla/control/simple_button.cpp +++ b/src/supla/control/simple_button.cpp @@ -30,18 +30,19 @@ Supla::Control::ButtonState::ButtonState(int pin, bool pullUp, bool invertLogic) } int Supla::Control::ButtonState::update() { - if (millis() - debounceTimeMs > debounceDelayMs) { + unsigned long curMillis = millis(); + if (curMillis - debounceTimeMs > debounceDelayMs) { int currentState = Supla::Io::digitalRead(pin); if (currentState != prevState) { // If status is changed, then make sure that it will be kept at // least swNoiseFilterDelayMs ms to avoid noise if (currentState != newStatusCandidate) { newStatusCandidate = currentState; - filterTimeMs = millis(); - } else if (millis() - filterTimeMs > swNoiseFilterDelayMs) { + filterTimeMs = curMillis; + } else if (curMillis - filterTimeMs > swNoiseFilterDelayMs) { // If new status is kept at least swNoiseFilterDelayMs ms, then apply // change of status - debounceTimeMs = millis(); + debounceTimeMs = curMillis; prevState = currentState; if (currentState == valueOnPress()) { return TO_PRESSED; From 6feb639d86ab7161ffa2e64e0505d08dd864bb7d Mon Sep 17 00:00:00 2001 From: klew Date: Thu, 11 Feb 2021 23:24:39 +0100 Subject: [PATCH 18/38] Simple button tests --- .../test/ButtonTests/simple_button_tests.cpp | 110 +++++++++++++++++- src/supla/control/simple_button.cpp | 4 +- 2 files changed, 111 insertions(+), 3 deletions(-) diff --git a/extras/test/ButtonTests/simple_button_tests.cpp b/extras/test/ButtonTests/simple_button_tests.cpp index 59861677..6a608c65 100644 --- a/extras/test/ButtonTests/simple_button_tests.cpp +++ b/extras/test/ButtonTests/simple_button_tests.cpp @@ -57,23 +57,84 @@ TEST(SimpleButtonTests, PullupInitAndPress) { DigitalInterfaceMock ioMock; ActionHandlerMock mock1; + EXPECT_CALL(ioMock, pinMode(5, INPUT_PULLUP)); + EXPECT_CALL(ioMock, digitalRead(5)) + .WillOnce(Return(1)) + .WillOnce(Return(0)) // time 1000 - first read + .WillOnce(Return(0)) // second read, should be ignored + .WillOnce(Return(0)) // third read, should trigger on_press + .WillOnce(Return(0)) // time 1090 + .WillOnce(Return(1)) // time 1100 + .WillOnce(Return(1)) // time 1110 + .WillOnce(Return(1)) // time 1150 + ; + + EXPECT_CALL(time, millis) + .WillOnce(Return(1000)) // first read + .WillOnce(Return(1010)) // should be ignored by filtering time + .WillOnce(Return(1030)) // should trigger on press + .WillOnce(Return(1040)) // + .WillOnce(Return(1050)) // + .WillOnce(Return(1060)) // + .WillOnce(Return(1090)) // + .WillOnce(Return(1100)) // + .WillOnce(Return(1110)) // + .WillOnce(Return(1150)) // + ; + + + EXPECT_CALL(mock1, handleAction(Supla::ON_PRESS, 1)).Times(1); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, 2)).Times(2); + EXPECT_CALL(mock1, handleAction(Supla::ON_RELEASE, 3)).Times(1); + + Supla::Control::SimpleButton button(5, true, true); + button.onInit(); + button.addAction(1, mock1, Supla::ON_PRESS); + button.addAction(2, mock1, Supla::ON_CHANGE); + button.addAction(3, mock1, Supla::ON_RELEASE); + + button.onTimer(); + button.onTimer(); + button.onTimer(); + button.onTimer(); + button.onTimer(); + button.onTimer(); + button.onTimer(); + button.onTimer(); + button.onTimer(); + button.onTimer(); + +} + +TEST(SimpleButtonTests, PullupInitAndPressWithoutNoiseFilter) { + TimeInterfaceMock time; + DigitalInterfaceMock ioMock; + ActionHandlerMock mock1; + EXPECT_CALL(ioMock, pinMode(5, INPUT_PULLUP)); EXPECT_CALL(ioMock, digitalRead(5)) .WillOnce(Return(1)) .WillOnce(Return(0)) .WillOnce(Return(0)) + .WillOnce(Return(0)) + .WillOnce(Return(1)) ; EXPECT_CALL(time, millis) .WillOnce(Return(1000)) + .WillOnce(Return(1010)) .WillOnce(Return(1100)) + .WillOnce(Return(1200)) + .WillOnce(Return(1300)) ; EXPECT_CALL(mock1, handleAction(Supla::ON_PRESS, 1)).Times(1); - EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, 2)).Times(1); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, 2)).Times(2); + EXPECT_CALL(mock1, handleAction(Supla::ON_RELEASE, 3)).Times(1); Supla::Control::SimpleButton button(5, true, true); + button.setSwNoiseFilterDelay(0); button.onInit(); button.addAction(1, mock1, Supla::ON_PRESS); button.addAction(2, mock1, Supla::ON_CHANGE); @@ -81,6 +142,53 @@ TEST(SimpleButtonTests, PullupInitAndPress) { button.onTimer(); button.onTimer(); + button.onTimer(); + button.onTimer(); + button.onTimer(); + +} + +TEST(SimpleButtonTests, PullupInitAndPressWithoutDebounce) { + TimeInterfaceMock time; + DigitalInterfaceMock ioMock; + ActionHandlerMock mock1; + + EXPECT_CALL(ioMock, pinMode(5, INPUT_PULLUP)); + EXPECT_CALL(ioMock, digitalRead(5)) + .WillOnce(Return(1)) + + .WillOnce(Return(0)) + .WillOnce(Return(0)) + .WillOnce(Return(0)) + .WillOnce(Return(1)) + .WillOnce(Return(1)) + ; + + EXPECT_CALL(time, millis) + .WillOnce(Return(1000)) + .WillOnce(Return(1010)) + .WillOnce(Return(1100)) + .WillOnce(Return(1101)) + .WillOnce(Return(1145)) + ; + + + EXPECT_CALL(mock1, handleAction(Supla::ON_PRESS, 1)).Times(1); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, 2)).Times(2); + EXPECT_CALL(mock1, handleAction(Supla::ON_RELEASE, 3)).Times(1); + + Supla::Control::SimpleButton button(5, true, true); + button.setDebounceDelay(0); + button.onInit(); + button.addAction(1, mock1, Supla::ON_PRESS); + button.addAction(2, mock1, Supla::ON_CHANGE); + button.addAction(3, mock1, Supla::ON_RELEASE); + + button.onTimer(); + button.onTimer(); + button.onTimer(); + button.onTimer(); + button.onTimer(); } diff --git a/src/supla/control/simple_button.cpp b/src/supla/control/simple_button.cpp index 22ae4ca8..2bbbd387 100644 --- a/src/supla/control/simple_button.cpp +++ b/src/supla/control/simple_button.cpp @@ -31,12 +31,12 @@ Supla::Control::ButtonState::ButtonState(int pin, bool pullUp, bool invertLogic) int Supla::Control::ButtonState::update() { unsigned long curMillis = millis(); - if (curMillis - debounceTimeMs > debounceDelayMs) { + if (debounceDelayMs == 0 || curMillis - debounceTimeMs > debounceDelayMs) { int currentState = Supla::Io::digitalRead(pin); if (currentState != prevState) { // If status is changed, then make sure that it will be kept at // least swNoiseFilterDelayMs ms to avoid noise - if (currentState != newStatusCandidate) { + if (swNoiseFilterDelayMs != 0 && currentState != newStatusCandidate) { newStatusCandidate = currentState; filterTimeMs = curMillis; } else if (curMillis - filterTimeMs > swNoiseFilterDelayMs) { From 65842c6e8695ccadae28c16f96bc5ae3d2b88457 Mon Sep 17 00:00:00 2001 From: klew Date: Sun, 14 Feb 2021 23:55:08 +0100 Subject: [PATCH 19/38] Added configurable EEPROM size for ESP boards. Removed call to delay in timer based methods. Removed dependency to IPAddress class --- src/SuplaDevice.cpp | 27 ++++++++++++++++++--------- src/SuplaDevice.h | 2 -- src/supla/element.cpp | 1 - src/supla/network/esp_wifi.h | 2 +- src/supla/network/ethernet_shield.h | 2 +- src/supla/network/network.cpp | 4 ++-- src/supla/network/network.h | 6 ++---- src/supla/storage/eeprom.cpp | 10 ++++++++-- src/supla/storage/eeprom.h | 3 ++- src/supla/timer.cpp | 4 ++++ 10 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/SuplaDevice.cpp b/src/SuplaDevice.cpp index de04f397..90cf5f43 100644 --- a/src/SuplaDevice.cpp +++ b/src/SuplaDevice.cpp @@ -94,22 +94,27 @@ bool SuplaDeviceClass::begin(unsigned char version) { // Supla::Storage::LoadDeviceConfig(); // Supla::Storage::LoadElementConfig(); - // Pefrorm dry run of write state to validate stored state section with current - // device configuration - Serial.println(F("Validating storage state section with current device configuration")); + // Pefrorm dry run of write state to validate stored state section with + // current device configuration + Serial.println( + F("Validating storage state section with current device configuration")); Supla::Storage::PrepareState(true); for (auto element = Supla::Element::begin(); element != nullptr; - element = element->next()) { + element = element->next()) { element->onSaveState(); + delay(0); } // If state storage validation was successful, perform read state if (Supla::Storage::FinalizeSaveState()) { - Serial.println(F("Storage state section validation completed. Loading elements state...")); + Serial.println( + F("Storage state section validation completed. Loading elements " + "state...")); // Iterate all elements and load state Supla::Storage::PrepareState(); for (auto element = Supla::Element::begin(); element != nullptr; - element = element->next()) { + element = element->next()) { element->onLoadState(); + delay(0); } } @@ -117,12 +122,12 @@ bool SuplaDeviceClass::begin(unsigned char version) { for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { element->onInit(); + delay(0); } // Enable timers Supla::initTimers(); - bool emptyGuidDetected = true; for (int i = 0; i < SUPLA_GUID_SIZE; i++) { if (Supla::Channel::reg_dev.GUID[i] != 0) { @@ -244,6 +249,7 @@ void SuplaDeviceClass::iterate(void) { for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { element->iterateAlways(); + delay(0); } // Iterate all elements and saves state @@ -252,6 +258,7 @@ void SuplaDeviceClass::iterate(void) { for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { element->onSaveState(); + delay(0); } Supla::Storage::FinalizeSaveState(); } @@ -346,6 +353,7 @@ void SuplaDeviceClass::iterate(void) { if (!element->iterateConnected(srpc)) { break; } + delay(0); } last_iterate_time = millis(); @@ -501,7 +509,8 @@ void SuplaDeviceClass::setServer(const char *server) { Supla::Channel::reg_dev.ServerName, server, SUPLA_SERVER_NAME_MAXSIZE); } -void SuplaDeviceClass::onGetUserLocaltimeResult(TSDC_UserLocalTimeResult *result) { +void SuplaDeviceClass::onGetUserLocaltimeResult( + TSDC_UserLocalTimeResult *result) { if (clock) { clock->parseLocaltimeFromServer(result); } @@ -512,7 +521,7 @@ void SuplaDeviceClass::addClock(Supla::Clock *_clock) { clock = _clock; } -Supla::Clock * SuplaDeviceClass::getClock() { +Supla::Clock *SuplaDeviceClass::getClock() { return clock; } diff --git a/src/SuplaDevice.h b/src/SuplaDevice.h index 5c1067fe..0057105c 100644 --- a/src/SuplaDevice.h +++ b/src/SuplaDevice.h @@ -17,8 +17,6 @@ #ifndef SUPLADEVICE_H #define SUPLADEVICE_H -#include - #include "supla-common/proto.h" #include "supla/network/network.h" #include "supla/uptime.h" diff --git a/src/supla/element.cpp b/src/supla/element.cpp index 9d16c3ae..70fc8a11 100644 --- a/src/supla/element.cpp +++ b/src/supla/element.cpp @@ -65,7 +65,6 @@ Element *Element::getElementByChannelNumber(int channelNumber) { } Element *Element::next() { - delay(0); return nextPtr; } diff --git a/src/supla/network/esp_wifi.h b/src/supla/network/esp_wifi.h index c02e5090..b0d60130 100644 --- a/src/supla/network/esp_wifi.h +++ b/src/supla/network/esp_wifi.h @@ -44,7 +44,7 @@ class ESPWifi : public Supla::Network { public: ESPWifi(const char *wifiSsid = nullptr, const char *wifiPassword = nullptr, - IPAddress *ip = nullptr) + unsigned char *ip = nullptr) : Network(ip), client(nullptr), isSecured(true), wifiConfigured(false) { ssid[0] = '\0'; password[0] = '\0'; diff --git a/src/supla/network/ethernet_shield.h b/src/supla/network/ethernet_shield.h index 7d5e7e7a..683acf94 100644 --- a/src/supla/network/ethernet_shield.h +++ b/src/supla/network/ethernet_shield.h @@ -28,7 +28,7 @@ namespace Supla { class EthernetShield : public Supla::Network { public: - EthernetShield(uint8_t mac[6], IPAddress *ip = NULL) : Network(ip), isDeviceReady(false) { + EthernetShield(uint8_t mac[6], unsigned char *ip = NULL) : Network(ip), isDeviceReady(false) { memcpy(this->mac, mac, 6); } diff --git a/src/supla/network/network.cpp b/src/supla/network/network.cpp index 4cf98681..b8864583 100644 --- a/src/supla/network/network.cpp +++ b/src/supla/network/network.cpp @@ -145,7 +145,7 @@ void message_received(void *_srpc, } } -Network::Network(IPAddress *ip) { +Network::Network(unsigned char *ip) { lastSentMs = 0; srpc = NULL; lastPingTimeMs = 0; @@ -158,7 +158,7 @@ Network::Network(IPAddress *ip) { useLocalIp = false; } else { useLocalIp = true; - localIp = *ip; + memcpy(localIp, ip, 4); } } diff --git a/src/supla/network/network.h b/src/supla/network/network.h index 9316eca2..8e5a5da1 100644 --- a/src/supla/network/network.h +++ b/src/supla/network/network.h @@ -17,8 +17,6 @@ #ifndef _network_interface_h #define _network_interface_h -#include - #include "supla-common/log.h" #include "supla-common/proto.h" @@ -99,7 +97,7 @@ class Network { return false; } - Network(IPAddress *ip); + Network(uint8_t ip[4]); virtual int read(void *buf, int count) = 0; virtual int write(void *buf, int count) = 0; virtual int connect(const char *server, int port = -1) = 0; @@ -129,7 +127,7 @@ class Network { void *srpc; bool useLocalIp; - IPAddress localIp; + unsigned char localIp[4]; }; // Method passed to SRPC as a callback to read raw data from network interface diff --git a/src/supla/storage/eeprom.cpp b/src/supla/storage/eeprom.cpp index 79cd9f7b..8bba0afb 100644 --- a/src/supla/storage/eeprom.cpp +++ b/src/supla/storage/eeprom.cpp @@ -24,19 +24,25 @@ using namespace Supla; // By default, write to EEPROM every 3 min #define SUPLA_EEPROM_WRITING_PERIOD 3*60*1000 -Eeprom::Eeprom(unsigned int storageStartingOffset) +Eeprom::Eeprom(unsigned int storageStartingOffset, int reservedSize) : Storage(storageStartingOffset), - dataChanged(false) { + dataChanged(false), reservedSize(reservedSize) { setStateSavePeriod((unsigned long)SUPLA_EEPROM_WRITING_PERIOD); } bool Eeprom::init() { +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + if (reservedSize <= 0) { #if defined(ARDUINO_ARCH_ESP8266) EEPROM.begin(1024); #elif defined(ARDUINO_ARCH_ESP32) EEPROM.begin(512); #endif + } else { + EEPROM.begin(reservedSize); + } delay(15); +#endif return Storage::init(); } diff --git a/src/supla/storage/eeprom.h b/src/supla/storage/eeprom.h index b9699ada..2dc40bfe 100644 --- a/src/supla/storage/eeprom.h +++ b/src/supla/storage/eeprom.h @@ -23,7 +23,7 @@ namespace Supla { class Eeprom : public Storage { public: - Eeprom(unsigned int storageStartingOffset = 0); + Eeprom(unsigned int storageStartingOffset = 0, int reservedSize = -1); bool init(); void commit(); @@ -31,6 +31,7 @@ class Eeprom : public Storage { int readStorage(unsigned int, unsigned char *, int, bool); int writeStorage(unsigned int, const unsigned char *, int); + int reservedSize; bool dataChanged; }; diff --git a/src/supla/timer.cpp b/src/supla/timer.cpp index a0a50ec7..8a43039f 100644 --- a/src/supla/timer.cpp +++ b/src/supla/timer.cpp @@ -23,6 +23,10 @@ #include #endif +#ifdef ARDUINO_ARCH_ESP8266 +#include +#endif + namespace { #if defined(ARDUINO_ARCH_ESP8266) ETSTimer supla_esp_timer; From 544324c149a0e799f989ebbccc23852ad288fdf4 Mon Sep 17 00:00:00 2001 From: klew Date: Tue, 16 Feb 2021 00:20:49 +0100 Subject: [PATCH 20/38] ValidityTimeSec support for channels --- src/supla/channel.cpp | 13 ++++++++----- src/supla/channel.h | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/supla/channel.cpp b/src/supla/channel.cpp index 65a3ea03..c24e7b69 100644 --- a/src/supla/channel.cpp +++ b/src/supla/channel.cpp @@ -27,9 +27,7 @@ namespace Supla { unsigned long Channel::lastCommunicationTimeMs = 0; TDS_SuplaRegisterDevice_E Channel::reg_dev; -Channel::Channel() { - valueChanged = false; - channelNumber = -1; +Channel::Channel() : validityTimeSec(0), channelNumber(-1), valueChanged(false) { if (reg_dev.channel_count < SUPLA_CHANNELMAXCOUNT) { channelNumber = reg_dev.channel_count; @@ -214,8 +212,9 @@ void Channel::clearUpdateReady() { void Channel::sendUpdate(void *srpc) { clearUpdateReady(); - srpc_ds_async_channel_value_changed( - srpc, channelNumber, reg_dev.channels[channelNumber].value); + srpc_ds_async_channel_value_changed_c( + srpc, channelNumber, reg_dev.channels[channelNumber].value, + 0, validityTimeSec); // returns null for non-extended channels TSuplaChannelExtendedValue *extValue = getExtValue(); @@ -328,5 +327,9 @@ uint8_t Channel::getValueBrightness() { return reg_dev.channels[channelNumber].value[0]; } +void Channel::setValidityTimeSec(unsigned _supla_int_t timeSec) { + validityTimeSec = timeSec; + if (validityTimeSec > 0) unsetFlag(SUPLA_CHANNEL_FLAG_CHANNELSTATE); +} }; // namespace Supla diff --git a/src/supla/channel.h b/src/supla/channel.h index 453d6f88..75de480d 100644 --- a/src/supla/channel.h +++ b/src/supla/channel.h @@ -64,6 +64,7 @@ class Channel : public LocalAction { void setFlag(_supla_int_t flag); void unsetFlag(_supla_int_t flag); void setFuncList(_supla_int_t functions); + void setValidityTimeSec(unsigned _supla_int_t); void clearUpdateReady(); void sendUpdate(void *srpc); virtual TSuplaChannelExtendedValue *getExtValue(); @@ -76,7 +77,7 @@ class Channel : public LocalAction { bool valueChanged; int channelNumber; - + unsigned _supla_int_t validityTimeSec; }; }; // namespace Supla From 36eea8ce6c8833c58b952a243fb43fd6cbb003a5 Mon Sep 17 00:00:00 2001 From: klew Date: Tue, 16 Feb 2021 11:46:42 +0100 Subject: [PATCH 21/38] Adjusted srpc mock to "c" value changed method --- extras/test/CMakeLists.txt | 1 + extras/test/ChannelTests/channel_tests.cpp | 4 +-- extras/test/ElementTests/element_tests.cpp | 4 +-- .../SuplaDeviceTests/supla_device_tests.cpp | 36 +++++++++++++++++++ extras/test/doubles/srpc_mock.cpp | 7 ++-- extras/test/doubles/srpc_mock.h | 29 ++++++++++----- 6 files changed, 66 insertions(+), 15 deletions(-) create mode 100644 extras/test/SuplaDeviceTests/supla_device_tests.cpp diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index e2d48aa9..d1d31b8c 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -45,6 +45,7 @@ file(GLOB TEST_SRC ConditionTests/*.cpp RgbwDimmerTests/*.cpp ButtonTests/*.cpp + SuplaDeviceTests/*.cpp ) file(GLOB DOUBLE_SRC doubles/*.cpp) diff --git a/extras/test/ChannelTests/channel_tests.cpp b/extras/test/ChannelTests/channel_tests.cpp index 09008e7a..876ea091 100644 --- a/extras/test/ChannelTests/channel_tests.cpp +++ b/extras/test/ChannelTests/channel_tests.cpp @@ -283,8 +283,8 @@ TEST(ChannelTests, SendUpdateTest) { char array[SUPLA_CHANNELVALUE_SIZE] = {}; array[0] = 1; - EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(emptyArray))); - EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array))); + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(emptyArray), 0, 0)); + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array), 0, 0)); EXPECT_FALSE(channel.isUpdateReady()); channel.sendUpdate(nullptr); diff --git a/extras/test/ElementTests/element_tests.cpp b/extras/test/ElementTests/element_tests.cpp index 8526dc0a..b12fee32 100644 --- a/extras/test/ElementTests/element_tests.cpp +++ b/extras/test/ElementTests/element_tests.cpp @@ -163,8 +163,8 @@ TEST(ElementTests, ChannelElementMethods) { char array0[SUPLA_CHANNELVALUE_SIZE] = {}; char array1[SUPLA_CHANNELVALUE_SIZE] = {}; array1[0] = 1; - EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array1))); // value at #2 - EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array0))); // value at #5 + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array1), 0, 0)); // value at #2 + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array0), 0, 0)); // value at #5 EXPECT_EQ(el1.iterateConnected(nullptr), true); // #1 diff --git a/extras/test/SuplaDeviceTests/supla_device_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_tests.cpp new file mode 100644 index 00000000..613b3325 --- /dev/null +++ b/extras/test/SuplaDeviceTests/supla_device_tests.cpp @@ -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. +*/ + +#include +#include +#include +#include + +using ::testing::Return; + +class TimeInterfaceStub : public TimeInterface { + public: + virtual unsigned long millis() override { + static unsigned long value = 0; + value += 1000; + return value; + } +}; + +TEST(SuplaDeviceTests, SNoPullupInit) { + ASSERT_TRUE(true); +} + diff --git a/extras/test/doubles/srpc_mock.cpp b/extras/test/doubles/srpc_mock.cpp index 46d5e16c..1a35a3c1 100644 --- a/extras/test/doubles/srpc_mock.cpp +++ b/extras/test/doubles/srpc_mock.cpp @@ -23,10 +23,11 @@ _supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_extendedvalue_changed( return 0; } -_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed( - void *_srpc, unsigned char channel_number, char *value) { +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed_c( + void *_srpc, unsigned char channel_number, char *value, + unsigned char offline, unsigned _supla_int_t validity_time_sec) { std::vector vec(value, value + 8); - return SrpcInterface::instance->valueChanged(_srpc, channel_number, vec); + return SrpcInterface::instance->valueChanged(_srpc, channel_number, vec, offline, validity_time_sec); } SrpcInterface::SrpcInterface() { diff --git a/extras/test/doubles/srpc_mock.h b/extras/test/doubles/srpc_mock.h index 1c7d677c..b2e45bf7 100644 --- a/extras/test/doubles/srpc_mock.h +++ b/extras/test/doubles/srpc_mock.h @@ -19,21 +19,34 @@ #include #include + #include class SrpcInterface { - public: - SrpcInterface(); - virtual ~SrpcInterface(); + public: + SrpcInterface(); + virtual ~SrpcInterface(); + + virtual _supla_int_t valueChanged(void *srpc, + unsigned char channelNumber, + std::vector value, + unsigned char offline, + unsigned _supla_int_t + validity_time_sec) = 0; - virtual _supla_int_t valueChanged(void *srpc, unsigned char channelNumber, std::vector value) = 0; - - static SrpcInterface *instance; + static SrpcInterface *instance; }; class SrpcMock : public SrpcInterface { - public: - MOCK_METHOD(_supla_int_t, valueChanged, (void *, unsigned char, std::vector), (override)); + public: + MOCK_METHOD(_supla_int_t, + valueChanged, + (void *, + unsigned char, + std::vector, + unsigned char, + unsigned _supla_int_t), + (override)); }; #endif From 1bca4993504c24fd9c5fd6c7f8c2af44f198f329 Mon Sep 17 00:00:00 2001 From: klew Date: Thu, 18 Feb 2021 23:29:08 +0100 Subject: [PATCH 22/38] Tests for SuplaDeviceClass, few mocks added. Lib version increment --- .../SuplaDeviceTests/supla_device_tests.cpp | 235 +++++++++++++++++- extras/test/doubles/arduino_mock.cpp | 5 + extras/test/doubles/srpc_mock.cpp | 71 +++++- extras/test/doubles/srpc_mock.h | 28 +++ extras/test/doubles/timer_mock.cpp | 34 +++ extras/test/doubles/timer_mock.h | 32 +++ library.properties | 2 +- src/CMakeLists.txt | 4 + src/SuplaDevice.cpp | 57 +++-- src/supla/clock/clock.cpp | 3 +- src/supla/network/network.cpp | 5 + src/supla/network/network.h | 1 + src/supla/storage/storage.cpp | 8 +- src/supla/storage/storage.h | 4 +- 14 files changed, 452 insertions(+), 37 deletions(-) create mode 100644 extras/test/doubles/timer_mock.cpp create mode 100644 extras/test/doubles/timer_mock.h diff --git a/extras/test/SuplaDeviceTests/supla_device_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_tests.cpp index 613b3325..53718bdb 100644 --- a/extras/test/SuplaDeviceTests/supla_device_tests.cpp +++ b/extras/test/SuplaDeviceTests/supla_device_tests.cpp @@ -17,7 +17,11 @@ #include #include #include +#include +#include #include +#include +#include using ::testing::Return; @@ -30,7 +34,234 @@ class TimeInterfaceStub : public TimeInterface { } }; -TEST(SuplaDeviceTests, SNoPullupInit) { - ASSERT_TRUE(true); +TEST(SuplaDeviceTests, DefaultValuesTest) { + SuplaDeviceClass sd; + SrpcMock srpc; + TimerMock timer; + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_UNKNOWN); + EXPECT_EQ(sd.getClock(), nullptr); + + +} + +class ClockMock : public Supla::Clock { + public: + MOCK_METHOD(void, parseLocaltimeFromServer, (TSDC_UserLocalTimeResult *result), (override)); +}; + +TEST(SuplaDeviceTests, ClockMethods) { + SuplaDeviceClass sd; + ClockMock clock; + + ASSERT_EQ(sd.getClock(), nullptr); + sd.onGetUserLocaltimeResult(nullptr); + + sd.addClock(&clock); + ASSERT_EQ(sd.getClock(), &clock); + + EXPECT_CALL(clock, parseLocaltimeFromServer(nullptr)).Times(1); + + sd.onGetUserLocaltimeResult(nullptr); +} + +TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceNoElements) { + SuplaDeviceClass sd; + TimerMock timer; + + ASSERT_EQ(Supla::Element::begin(), nullptr); + EXPECT_CALL(timer, initTimers()); + + EXPECT_FALSE(sd.begin()); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_NETWORK_INTERFACE); +} + +class StorageMock2: public Supla::Storage { + public: + MOCK_METHOD(bool, init, (), (override)); + MOCK_METHOD(bool, prepareState, (bool), (override)); + MOCK_METHOD(bool, finalizeSaveState, (), (override)); + MOCK_METHOD(void, commit, (), (override)); + MOCK_METHOD(int, readStorage, (unsigned int, unsigned char *, int, bool), (override)); + MOCK_METHOD(int, writeStorage, (unsigned int, const unsigned char *, int), (override)); + +}; + +TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceNoElementsWithStorage) { + ::testing::InSequence seq; + SuplaDeviceClass sd; + TimerMock timer; + StorageMock2 storage; + + ASSERT_EQ(Supla::Element::begin(), nullptr); + EXPECT_CALL(storage, init()); + EXPECT_CALL(storage, prepareState(true)).WillOnce(Return(true));; + EXPECT_CALL(storage, finalizeSaveState()).WillOnce(Return(false)); + + EXPECT_CALL(timer, initTimers()); + + EXPECT_FALSE(sd.begin()); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_NETWORK_INTERFACE); +} + +TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceNoElementsWithStorageAndDataLoadAttempt) { + ::testing::InSequence seq; + SuplaDeviceClass sd; + TimerMock timer; + StorageMock2 storage; + + ASSERT_EQ(Supla::Element::begin(), nullptr); + EXPECT_CALL(storage, init()); + EXPECT_CALL(storage, prepareState(true)).WillOnce(Return(true)); + EXPECT_CALL(storage, finalizeSaveState()).WillOnce(Return(true)); + EXPECT_CALL(storage, prepareState(false)); + + EXPECT_CALL(timer, initTimers()); + + EXPECT_FALSE(sd.begin()); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_NETWORK_INTERFACE); +} + +class ElementMock : public Supla::Element { + public: + MOCK_METHOD(void, onInit, (), (override)); + MOCK_METHOD(void, onLoadState, (), (override)); + MOCK_METHOD(void, onSaveState, (), (override)); + MOCK_METHOD(void, iterateAlways, (), (override)); + MOCK_METHOD(bool, iterateConnected, (void *), (override)); + MOCK_METHOD(void, onTimer, (), (override)); + MOCK_METHOD(void, onFastTimer, (), (override)); + MOCK_METHOD(int, handleNewValueFromServer, (TSD_SuplaChannelNewValue *), (override)); + MOCK_METHOD(void, handleGetChannelState, (TDSC_ChannelState &), (override)); + MOCK_METHOD(int, handleCalcfgFromServer, (TSD_DeviceCalCfgRequest *), (override)); + +}; + +TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceWithElements) { + ::testing::InSequence seq; + SuplaDeviceClass sd; + TimerMock timer; + ElementMock el1; + ElementMock el2; + + ASSERT_NE(Supla::Element::begin(), nullptr); + + EXPECT_CALL(el1, onInit()); + EXPECT_CALL(el2, onInit()); + + EXPECT_CALL(timer, initTimers()); + + EXPECT_FALSE(sd.begin()); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_NETWORK_INTERFACE); +} + +TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceWithElementsWithStorage) { + ::testing::InSequence seq; + StorageMock2 storage; + SuplaDeviceClass sd; + TimerMock timer; + ElementMock el1; + ElementMock el2; + + ASSERT_NE(Supla::Element::begin(), nullptr); + + EXPECT_CALL(storage, init()); + EXPECT_CALL(storage, prepareState(true)).WillOnce(Return(true)); + EXPECT_CALL(el1, onSaveState()); + EXPECT_CALL(el2, onSaveState()); + + EXPECT_CALL(storage, finalizeSaveState()).WillOnce(Return(true)); + EXPECT_CALL(storage, prepareState(false)); + EXPECT_CALL(el1, onLoadState()); + EXPECT_CALL(el2, onLoadState()); + + EXPECT_CALL(el1, onInit()); + EXPECT_CALL(el2, onInit()); + + EXPECT_CALL(timer, initTimers()); + + EXPECT_FALSE(sd.begin()); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_NETWORK_INTERFACE); +} + +class NetworkMock : public Supla::Network { + public: + NetworkMock() : Supla::Network(nullptr) {}; + MOCK_METHOD(int, read, (void *, int ), (override)); + MOCK_METHOD(int, write, (void *, int ), (override)); + MOCK_METHOD(int, connect, (const char *, int), (override)); + MOCK_METHOD(bool, connected, (), (override)); + MOCK_METHOD(void, disconnect, (), (override)); + MOCK_METHOD(void, setup, (), (override)); + MOCK_METHOD(void, setTimeout, (int), (override)); + + MOCK_METHOD(bool, isReady, (), (override)); + MOCK_METHOD(bool, iterate, (), (override)); + MOCK_METHOD(bool, ping, (), (override)); + +}; + +TEST(SuplaDeviceTEsts, BeginStopsAtEmptyGUID) { + ::testing::InSequence seq; + NetworkMock net; + TimerMock timer; + + SuplaDeviceClass sd; + + EXPECT_CALL(timer, initTimers()); + + EXPECT_FALSE(sd.begin()); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_INVALID_GUID); } +TEST(SuplaDeviceTEsts, BeginStopsAtEmptyServer) { + ::testing::InSequence seq; + NetworkMock net; + TimerMock timer; + + SuplaDeviceClass sd; + + EXPECT_CALL(timer, initTimers()); + + char GUID[SUPLA_GUID_SIZE] = {1}; + sd.setGUID(GUID); + EXPECT_FALSE(sd.begin()); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_UNKNOWN_SERVER_ADDRESS); +} + +TEST(SuplaDeviceTEsts, BeginStopsAtEmptyEmail) { + ::testing::InSequence seq; + NetworkMock net; + TimerMock timer; + + SuplaDeviceClass sd; + + EXPECT_CALL(timer, initTimers()); + + char GUID[SUPLA_GUID_SIZE] = {1}; + sd.setGUID(GUID); + sd.setServer("supla.rulez"); + EXPECT_FALSE(sd.begin()); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_CREDENTIALS); +} + +/* +TEST(SuplaDeviceTEsts, BeginStopsAtEmptyAuthkey) { + ::testing::InSequence seq; + NetworkMock net; + TimerMock timer; + + SuplaDeviceClass sd; + + EXPECT_CALL(timer, initTimers()); + + char GUID[SUPLA_GUID_SIZE] = {1}; + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {2}; + sd.setGUID(GUID); + sd.setServer("supla.rulez"); + sd.setEmail("john@supla"); + sd.setAuthKey(AUTHKEY); + EXPECT_TRUE(sd.begin()); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_INITIALIZED); +} +*/ diff --git a/extras/test/doubles/arduino_mock.cpp b/extras/test/doubles/arduino_mock.cpp index a44b6e67..4dbc4916 100644 --- a/extras/test/doubles/arduino_mock.cpp +++ b/extras/test/doubles/arduino_mock.cpp @@ -42,22 +42,27 @@ TimeInterface::~TimeInterface() { TimeInterface *TimeInterface::instance = nullptr; void analogWrite(uint8_t pin, int val) { + assert(DigitalInterface::instance); DigitalInterface::instance->analogWrite(pin, val); } void digitalWrite(uint8_t pin, uint8_t val) { + assert(DigitalInterface::instance); DigitalInterface::instance->digitalWrite(pin, val); } int digitalRead(uint8_t pin) { + assert(DigitalInterface::instance); return DigitalInterface::instance->digitalRead(pin); } void pinMode(uint8_t pin, uint8_t mode) { + assert(DigitalInterface::instance); DigitalInterface::instance->pinMode(pin, mode); } unsigned long millis() { + assert(TimeInterface::instance); return TimeInterface::instance->millis(); } diff --git a/extras/test/doubles/srpc_mock.cpp b/extras/test/doubles/srpc_mock.cpp index 1a35a3c1..6b258c51 100644 --- a/extras/test/doubles/srpc_mock.cpp +++ b/extras/test/doubles/srpc_mock.cpp @@ -17,19 +17,86 @@ #include #include "srpc_mock.h" -_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_extendedvalue_changed( +_supla_int_t srpc_ds_async_channel_extendedvalue_changed( void *_srpc, unsigned char channel_number, TSuplaChannelExtendedValue *value) { return 0; } -_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed_c( +_supla_int_t srpc_ds_async_channel_value_changed_c( void *_srpc, unsigned char channel_number, char *value, unsigned char offline, unsigned _supla_int_t validity_time_sec) { + assert(SrpcInterface::instance); std::vector vec(value, value + 8); return SrpcInterface::instance->valueChanged(_srpc, channel_number, vec, offline, validity_time_sec); } +_supla_int_t srpc_dcs_async_set_activity_timeout( + void *_srpc, TDCS_SuplaSetActivityTimeout *dcs_set_activity_timeout) { + assert(SrpcInterface::instance); + return SrpcInterface::instance->srpc_dcs_async_set_activity_timeout(_srpc, dcs_set_activity_timeout); +} + +void srpc_params_init(TsrpcParams *params) { + assert(SrpcInterface::instance); + SrpcInterface::instance->srpc_params_init(params); +} + +_supla_int_t srpc_ds_async_set_channel_result(void *_srpc, unsigned char ChannelNumber, _supla_int_t SenderID, char Success) { + assert(SrpcInterface::instance); + return SrpcInterface::instance->srpc_ds_async_set_channel_result(_srpc, ChannelNumber, SenderID, Success); +} + +_supla_int_t srpc_ds_async_device_calcfg_result(void *_srpc, TDS_DeviceCalCfgResult *result) { + assert(SrpcInterface::instance); + return SrpcInterface::instance->srpc_ds_async_device_calcfg_result(_srpc, result); +} + +void *srpc_init(TsrpcParams *params) { + assert(SrpcInterface::instance); + return SrpcInterface::instance->srpc_init(params); +} + +void srpc_rd_free(TsrpcReceivedData *rd) { + assert(SrpcInterface::instance); + SrpcInterface::instance->srpc_rd_free(rd); +} + +char srpc_getdata(void *_srpc, TsrpcReceivedData *rd, unsigned _supla_int_t rr_id) { + assert(SrpcInterface::instance); + return SrpcInterface::instance->srpc_getdata(_srpc, rd, rr_id); +} + +char srpc_iterate(void *_srpc) { + assert(SrpcInterface::instance); + return SrpcInterface::instance->srpc_iterate(_srpc); +} + +void srpc_set_proto_version(void *_srpc, unsigned char version) { + assert(SrpcInterface::instance); + SrpcInterface::instance->srpc_set_proto_version(_srpc, version); +} + +_supla_int_t srpc_ds_async_registerdevice_e(void *_srpc, TDS_SuplaRegisterDevice_E *registerdevice) { + assert(SrpcInterface::instance); + return SrpcInterface::instance->srpc_ds_async_registerdevice_e(_srpc, registerdevice); +} + +_supla_int_t srpc_dcs_async_ping_server(void *_srpc) { + assert(SrpcInterface::instance); + return SrpcInterface::instance->srpc_dcs_async_ping_server(_srpc); +} + +_supla_int_t srpc_csd_async_channel_state_result(void *_srpc, TDSC_ChannelState *state) { + assert(SrpcInterface::instance); + return SrpcInterface::instance->srpc_csd_async_channel_state_result(_srpc, state); +} + +_supla_int_t srpc_dcs_async_get_user_localtime(void *_srpc) { + assert(SrpcInterface::instance); + return SrpcInterface::instance->srpc_dcs_async_get_user_localtime(_srpc); +} + SrpcInterface::SrpcInterface() { instance = this; } diff --git a/extras/test/doubles/srpc_mock.h b/extras/test/doubles/srpc_mock.h index b2e45bf7..68e262fb 100644 --- a/extras/test/doubles/srpc_mock.h +++ b/extras/test/doubles/srpc_mock.h @@ -19,6 +19,7 @@ #include #include +#include #include @@ -34,6 +35,20 @@ class SrpcInterface { unsigned _supla_int_t validity_time_sec) = 0; + virtual _supla_int_t srpc_dcs_async_set_activity_timeout(void *_srpc, TDCS_SuplaSetActivityTimeout *dcs_set_activity_timeout) = 0; + virtual void srpc_params_init(TsrpcParams *params) = 0; + virtual _supla_int_t srpc_ds_async_set_channel_result(void *_srpc, unsigned char ChannelNumber, _supla_int_t SenderID, char Success) = 0; + virtual _supla_int_t srpc_ds_async_device_calcfg_result(void *_srpc, TDS_DeviceCalCfgResult *result) = 0; + virtual void *srpc_init(TsrpcParams *params) = 0; + virtual void srpc_rd_free(TsrpcReceivedData *rd) = 0; + virtual char srpc_getdata(void *_srpc, TsrpcReceivedData *rd, unsigned _supla_int_t rr_id) = 0; + virtual char srpc_iterate(void *_srpc) = 0; + virtual void srpc_set_proto_version(void *_srpc, unsigned char version) = 0; + virtual _supla_int_t srpc_ds_async_registerdevice_e(void *_srpc, TDS_SuplaRegisterDevice_E *registerdevice) = 0; + virtual _supla_int_t srpc_dcs_async_ping_server(void *_srpc) = 0; + virtual _supla_int_t srpc_csd_async_channel_state_result(void *_srpc, TDSC_ChannelState *state) = 0; + virtual _supla_int_t srpc_dcs_async_get_user_localtime(void *_srpc) = 0; + static SrpcInterface *instance; }; @@ -47,6 +62,19 @@ class SrpcMock : public SrpcInterface { unsigned char, unsigned _supla_int_t), (override)); + MOCK_METHOD(_supla_int_t, srpc_dcs_async_set_activity_timeout, (void *, TDCS_SuplaSetActivityTimeout *), (override)); + MOCK_METHOD(void, srpc_params_init, (TsrpcParams *), (override)); + MOCK_METHOD(_supla_int_t, srpc_ds_async_set_channel_result, (void *, unsigned char, _supla_int_t, char), (override)); + MOCK_METHOD(_supla_int_t, srpc_ds_async_device_calcfg_result, (void *, TDS_DeviceCalCfgResult *), (override)); + MOCK_METHOD((void *), srpc_init, (TsrpcParams *), (override)); + MOCK_METHOD(void, srpc_rd_free, (TsrpcReceivedData *), (override)); + MOCK_METHOD(char, srpc_getdata, (void *, TsrpcReceivedData *, unsigned _supla_int_t), (override)); + MOCK_METHOD(char, srpc_iterate, (void *), (override)); + MOCK_METHOD(void, srpc_set_proto_version, (void *, unsigned char), (override)); + MOCK_METHOD(_supla_int_t, srpc_ds_async_registerdevice_e, (void *, TDS_SuplaRegisterDevice_E *), (override)); + MOCK_METHOD(_supla_int_t, srpc_dcs_async_ping_server, (void *), (override)); + MOCK_METHOD(_supla_int_t, srpc_csd_async_channel_state_result, (void *, TDSC_ChannelState *), (override)); + MOCK_METHOD(_supla_int_t, srpc_dcs_async_get_user_localtime, (void *), (override)); }; #endif diff --git a/extras/test/doubles/timer_mock.cpp b/extras/test/doubles/timer_mock.cpp new file mode 100644 index 00000000..782dce94 --- /dev/null +++ b/extras/test/doubles/timer_mock.cpp @@ -0,0 +1,34 @@ +/* + 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 +#include + +TimerInterface *timerInterfaceInstance = nullptr; + +TimerInterface::TimerInterface() { + timerInterfaceInstance = this; +} + +TimerInterface::~TimerInterface() { + timerInterfaceInstance = nullptr; +} + + +void Supla::initTimers() { + assert(timerInterfaceInstance); + timerInterfaceInstance->initTimers(); +} diff --git a/extras/test/doubles/timer_mock.h b/extras/test/doubles/timer_mock.h new file mode 100644 index 00000000..85ba1783 --- /dev/null +++ b/extras/test/doubles/timer_mock.h @@ -0,0 +1,32 @@ +/* + 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 +#include + +class TimerInterface { + public: + TimerInterface(); + virtual ~TimerInterface(); + virtual void initTimers() = 0; +}; + +class TimerMock : public TimerInterface { + public: + MOCK_METHOD((void), initTimers, (), (override)); +}; + + diff --git a/library.properties b/library.properties index 87d96835..d27cea12 100644 --- a/library.properties +++ b/library.properties @@ -5,7 +5,7 @@ sentence=Library enables you to connect the device to the SUPLA automation syste paragraph=It provides easy interface for adding various sensors, relays, buttons, roller shutters, etc. that can be controlled via SUPLA Cloud and application on mobile device. url=https://github.com/SUPLA/arduino architectures=avr,esp32,esp8266 -version=2.3.2 +version=2.3.3 dependencies= core-dependencies=arduino (>=1.5.0) category=Communication diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 55931cc9..96e7d10e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,6 +29,10 @@ set(SRCS supla/conditions/on_between_eq.cpp supla/conditions/on_equal.cpp + SuplaDevice.cpp + supla/network/network.cpp + supla/clock/clock.cpp + ) add_library(supladevicelib SHARED ${SRCS}) diff --git a/src/SuplaDevice.cpp b/src/SuplaDevice.cpp index 90cf5f43..f60b1659 100644 --- a/src/SuplaDevice.cpp +++ b/src/SuplaDevice.cpp @@ -16,6 +16,8 @@ #include +#include + #include "SuplaDevice.h" #include "supla-common/IEEE754tools.h" #include "supla-common/log.h" @@ -41,7 +43,9 @@ SuplaDeviceClass::SuplaDeviceClass() : port(-1), connectionFailCounter(0), networkIsNotReadyCounter(0), - currentStatus(STATUS_UNKNOWN) { + currentStatus(STATUS_UNKNOWN), + clock(nullptr), + impl_arduino_status(nullptr) { srpc = NULL; registered = 0; last_iterate_time = 0; @@ -86,41 +90,39 @@ bool SuplaDeviceClass::begin(unsigned char version) { Supla::Storage::Init(); - if (Supla::Network::Instance() == NULL) { - status(STATUS_MISSING_NETWORK_INTERFACE, "Network Interface not defined!"); - return false; - } - // Supla::Storage::LoadDeviceConfig(); // Supla::Storage::LoadElementConfig(); // Pefrorm dry run of write state to validate stored state section with // current device configuration - Serial.println( - F("Validating storage state section with current device configuration")); - Supla::Storage::PrepareState(true); - for (auto element = Supla::Element::begin(); element != nullptr; - element = element->next()) { - element->onSaveState(); - delay(0); - } - // If state storage validation was successful, perform read state - if (Supla::Storage::FinalizeSaveState()) { + if (Supla::Storage::PrepareState(true)) { Serial.println( - F("Storage state section validation completed. Loading elements " - "state...")); - // Iterate all elements and load state - Supla::Storage::PrepareState(); + F("Validating storage state section with current device configuration")); for (auto element = Supla::Element::begin(); element != nullptr; - element = element->next()) { - element->onLoadState(); + element = element->next()) { + element->onSaveState(); delay(0); } + // If state storage validation was successful, perform read state + if (Supla::Storage::FinalizeSaveState()) { + Serial.println( + F("Storage state section validation completed. Loading elements " + "state...")); + // Iterate all elements and load state + Supla::Storage::PrepareState(); + for (auto element = Supla::Element::begin(); element != nullptr; + element = element->next()) { + element->onLoadState(); + delay(0); + } + } + } else { + Serial.println(F("Storage not found. Running without state memory")); } // Initialize elements for (auto element = Supla::Element::begin(); element != nullptr; - element = element->next()) { + element = element->next()) { element->onInit(); delay(0); } @@ -128,6 +130,11 @@ bool SuplaDeviceClass::begin(unsigned char version) { // Enable timers Supla::initTimers(); + if (Supla::Network::Instance() == NULL) { + status(STATUS_MISSING_NETWORK_INTERFACE, "Network Interface not defined!"); + return false; + } + bool emptyGuidDetected = true; for (int i = 0; i < SUPLA_GUID_SIZE; i++) { if (Supla::Channel::reg_dev.GUID[i] != 0) { @@ -175,8 +182,8 @@ bool SuplaDeviceClass::begin(unsigned char version) { if (strnlen(Supla::Channel::reg_dev.SoftVer, SUPLA_SOFTVER_MAXSIZE) == 0) { setString(Supla::Channel::reg_dev.SoftVer, - "User SW, lib 2.3.2", - SUPLA_SOFTVER_MAXSIZE); + "User SW, lib 2.3.3", + SUPLA_SOFTVER_MAXSIZE); } Serial.println(F("Initializing network layer")); diff --git a/src/supla/clock/clock.cpp b/src/supla/clock/clock.cpp index ffcd3f4b..1a329e20 100644 --- a/src/supla/clock/clock.cpp +++ b/src/supla/clock/clock.cpp @@ -82,8 +82,7 @@ int Clock::getSec() { void Clock::parseLocaltimeFromServer(TSDC_UserLocalTimeResult *result) { - struct tm timeinfo; - memset(&timeinfo, 0, sizeof(timeinfo)); + struct tm timeinfo{}; Serial.print(F("Current local time: ")); Serial.print(getYear()); diff --git a/src/supla/network/network.cpp b/src/supla/network/network.cpp index b8864583..6e7bf18b 100644 --- a/src/supla/network/network.cpp +++ b/src/supla/network/network.cpp @@ -14,6 +14,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include +#include #include "SuplaDevice.h" #include "supla-common/log.h" @@ -162,6 +163,10 @@ Network::Network(unsigned char *ip) { } } +Network::~Network() { + netIntf = nullptr; +} + bool Network::iterate() { return false; } diff --git a/src/supla/network/network.h b/src/supla/network/network.h index 8e5a5da1..7a054cb3 100644 --- a/src/supla/network/network.h +++ b/src/supla/network/network.h @@ -98,6 +98,7 @@ class Network { } Network(uint8_t ip[4]); + virtual ~Network(); virtual int read(void *buf, int count) = 0; virtual int write(void *buf, int count) = 0; virtual int connect(const char *server, int port = -1) = 0; diff --git a/src/supla/storage/storage.cpp b/src/supla/storage/storage.cpp index 9f78faf0..0ea03e1e 100644 --- a/src/supla/storage/storage.cpp +++ b/src/supla/storage/storage.cpp @@ -64,10 +64,11 @@ bool Storage::LoadElementConfig() { return false; } -void Storage::PrepareState(bool dryRun) { +bool Storage::PrepareState(bool dryRun) { if (Instance()) { - Instance()->prepareState(dryRun); + return Instance()->prepareState(dryRun); } + return false; } bool Storage::FinalizeSaveState() { @@ -111,10 +112,11 @@ Storage::~Storage() { instance = nullptr; } -void Storage::prepareState(bool performDryRun) { +bool Storage::prepareState(bool performDryRun) { dryRun = performDryRun; newSectionSize = 0; currentStateOffset = elementStateOffset + sizeof(SectionPreamble); + return true; } bool Storage::readState(unsigned char *buf, int size) { diff --git a/src/supla/storage/storage.h b/src/supla/storage/storage.h index 0537749c..4b450437 100644 --- a/src/supla/storage/storage.h +++ b/src/supla/storage/storage.h @@ -33,7 +33,7 @@ class Storage { static bool WriteState(const unsigned char *, int); static bool LoadDeviceConfig(); static bool LoadElementConfig(); - static void PrepareState(bool dryRun = false); + static bool PrepareState(bool dryRun = false); static bool FinalizeSaveState(); static bool SaveStateAllowed(unsigned long); static void ScheduleSave(unsigned long delayMs); @@ -50,7 +50,7 @@ class Storage { virtual bool loadDeviceConfig(); virtual bool loadElementConfig(); - virtual void prepareState(bool performDryRun); + virtual bool prepareState(bool performDryRun); virtual bool finalizeSaveState(); virtual bool saveStateAllowed(unsigned long); virtual void scheduleSave(unsigned long delayMs); From 6af21ab7a63722edd39987b8078a68336f083262 Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 19 Feb 2021 12:31:03 +0100 Subject: [PATCH 23/38] Added more tests for SuplaDeviceClass. Removed srpc pointer from Network --- .../SuplaDeviceTests/supla_device_tests.cpp | 31 +++++++++++++++++-- src/SuplaDevice.cpp | 3 +- src/supla/network/network.cpp | 7 +---- src/supla/network/network.h | 6 ++-- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/extras/test/SuplaDeviceTests/supla_device_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_tests.cpp index 53718bdb..58f933e8 100644 --- a/extras/test/SuplaDeviceTests/supla_device_tests.cpp +++ b/extras/test/SuplaDeviceTests/supla_device_tests.cpp @@ -24,6 +24,7 @@ #include using ::testing::Return; +using ::testing::_; class TimeInterfaceStub : public TimeInterface { public: @@ -197,7 +198,7 @@ class NetworkMock : public Supla::Network { MOCK_METHOD(bool, isReady, (), (override)); MOCK_METHOD(bool, iterate, (), (override)); - MOCK_METHOD(bool, ping, (), (override)); + MOCK_METHOD(bool, ping, (void *), (override)); }; @@ -245,7 +246,7 @@ TEST(SuplaDeviceTEsts, BeginStopsAtEmptyEmail) { EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_CREDENTIALS); } -/* + TEST(SuplaDeviceTEsts, BeginStopsAtEmptyAuthkey) { ::testing::InSequence seq; NetworkMock net; @@ -255,6 +256,30 @@ TEST(SuplaDeviceTEsts, BeginStopsAtEmptyAuthkey) { EXPECT_CALL(timer, initTimers()); + char GUID[SUPLA_GUID_SIZE] = {1}; + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {2}; + sd.setGUID(GUID); + sd.setServer("supla.rulez"); + sd.setEmail("john@supla"); + EXPECT_FALSE(sd.begin()); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_CREDENTIALS); +} + +TEST(SuplaDeviceTEsts, SuccessfulBegin) { + ::testing::InSequence seq; + SrpcMock srpc; + NetworkMock net; + TimerMock timer; + + SuplaDeviceClass sd; + int dummy; + + EXPECT_CALL(timer, initTimers()); + EXPECT_CALL(net, setup()); + EXPECT_CALL(srpc, srpc_params_init(_)); + EXPECT_CALL(srpc, srpc_init(_)).WillOnce(Return(&dummy)); + EXPECT_CALL(srpc, srpc_set_proto_version(&dummy, 12)); + char GUID[SUPLA_GUID_SIZE] = {1}; char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {2}; sd.setGUID(GUID); @@ -264,4 +289,4 @@ TEST(SuplaDeviceTEsts, BeginStopsAtEmptyAuthkey) { EXPECT_TRUE(sd.begin()); EXPECT_EQ(sd.getCurrentStatus(), STATUS_INITIALIZED); } -*/ + diff --git a/src/SuplaDevice.cpp b/src/SuplaDevice.cpp index f60b1659..eeb00a5b 100644 --- a/src/SuplaDevice.cpp +++ b/src/SuplaDevice.cpp @@ -197,7 +197,6 @@ bool SuplaDeviceClass::begin(unsigned char version) { srpc_params.user_params = this; srpc = srpc_init(&srpc_params); - Supla::Network::SetSrpc(srpc); // Set Supla protocol interface version srpc_set_proto_version(srpc, version); @@ -346,7 +345,7 @@ void SuplaDeviceClass::iterate(void) { } } else if (registered == 1) { - if (Supla::Network::Ping() == false) { + if (Supla::Network::Ping(srpc) == false) { uptime.setConnectionLostCause( SUPLA_LASTCONNECTIONRESETCAUSE_ACTIVITY_TIMEOUT); supla_log(LOG_DEBUG, "TIMEOUT - lost connection with server"); diff --git a/src/supla/network/network.cpp b/src/supla/network/network.cpp index 6e7bf18b..b27e00b7 100644 --- a/src/supla/network/network.cpp +++ b/src/supla/network/network.cpp @@ -148,7 +148,6 @@ void message_received(void *_srpc, Network::Network(unsigned char *ip) { lastSentMs = 0; - srpc = NULL; lastPingTimeMs = 0; serverActivityTimeoutS = 30; lastResponseMs = 0; @@ -179,11 +178,7 @@ void Network::updateLastResponse() { lastResponseMs = millis(); } -void Network::setSrpc(void *_srpc) { - srpc = _srpc; -} - -bool Network::ping() { +bool Network::ping(void *srpc) { _supla_int64_t _millis = millis(); // If time from last response is longer than "server_activity_timeout + 10 s", // then inform about failure in communication diff --git a/src/supla/network/network.h b/src/supla/network/network.h index 7a054cb3..85a2ac44 100644 --- a/src/supla/network/network.h +++ b/src/supla/network/network.h @@ -90,9 +90,9 @@ class Network { } } - static bool Ping() { + static bool Ping(void *srpc) { if (Instance() != NULL) { - return Instance()->ping(); + return Instance()->ping(srpc); } return false; } @@ -109,7 +109,7 @@ class Network { virtual bool isReady() = 0; virtual bool iterate(); - virtual bool ping(); + virtual bool ping(void *); virtual void fillStateData(TDSC_ChannelState &channelState); From 4d302aafa510156313ecb423bdcbfb37271d0490 Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 19 Feb 2021 13:00:44 +0100 Subject: [PATCH 24/38] Drone: -j32 --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 688641c7..da5b40d6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,7 +8,7 @@ steps: - mkdir extras/test/build - cd extras/test/build - cmake .. - - make + - make -j32 - name: test image: rikorose/gcc-cmake From 61e407f8b706c566883e3e78679c39a2323dca12 Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 19 Feb 2021 13:40:45 +0100 Subject: [PATCH 25/38] Added cleanup fixtures to remove dependencies between test runs. Added timestamp to drone configuration --- .drone.yml | 6 +-- extras/test/ElementTests/element_tests.cpp | 22 +++++++++-- .../SuplaDeviceTests/supla_device_tests.cpp | 37 +++++++++++++------ 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/.drone.yml b/.drone.yml index da5b40d6..c2fdb304 100644 --- a/.drone.yml +++ b/.drone.yml @@ -7,11 +7,11 @@ steps: commands: - mkdir extras/test/build - cd extras/test/build - - cmake .. - - make -j32 + - cmake .. | ts '[%Y-%m-%d %H:%M:%S]' + - make -j32 | ts '[%Y-%m-%d %H:%M:%S]' - name: test image: rikorose/gcc-cmake commands: - cd extras/test/build - - ./supladevicetests + - ./supladevicetests --gtest_repeat=50 --gtest_shuffle | ts '[%Y-%m-%d %H:%M:%S]' diff --git a/extras/test/ElementTests/element_tests.cpp b/extras/test/ElementTests/element_tests.cpp index b12fee32..4b5a7c5c 100644 --- a/extras/test/ElementTests/element_tests.cpp +++ b/extras/test/ElementTests/element_tests.cpp @@ -24,6 +24,20 @@ using ::testing::Return; using ::testing::ElementsAreArray; +class ElementTests : public ::testing::Test { + protected: + virtual void SetUp() { + Supla::Channel::lastCommunicationTimeMs = 0; + memset(&(Supla::Channel::reg_dev), 0, sizeof(Supla::Channel::reg_dev)); + } + virtual void TearDown() { + Supla::Channel::lastCommunicationTimeMs = 0; + memset(&(Supla::Channel::reg_dev), 0, sizeof(Supla::Channel::reg_dev)); + } + +}; + + class ElementWithChannel : public Supla::Element { public: Supla::Channel *getChannel() { @@ -32,7 +46,7 @@ class ElementWithChannel : public Supla::Element { Supla::Channel channel; }; -TEST(ElementTests, ElementEmptyListTests) { +TEST_F(ElementTests, ElementEmptyListTests) { EXPECT_EQ(Supla::Element::begin(), nullptr); EXPECT_EQ(Supla::Element::last(), nullptr); EXPECT_EQ(Supla::Element::getElementByChannelNumber(0), nullptr); @@ -40,7 +54,7 @@ TEST(ElementTests, ElementEmptyListTests) { EXPECT_EQ(Supla::Element::getElementByChannelNumber(10), nullptr); } -TEST(ElementTests, ElementListAdding) { +TEST_F(ElementTests, ElementListAdding) { auto el1 = new Supla::Element; EXPECT_EQ(Supla::Element::begin(), el1); @@ -93,7 +107,7 @@ TEST(ElementTests, ElementListAdding) { } -TEST(ElementTests, NoChannelElementMethods) { +TEST_F(ElementTests, NoChannelElementMethods) { Supla::Element el1; // those methods are empty, so just call to make sure that they do nothing and don't crash @@ -119,7 +133,7 @@ TEST(ElementTests, NoChannelElementMethods) { EXPECT_EQ(el1.handleCalcfgFromServer(nullptr), SUPLA_CALCFG_RESULT_NOT_SUPPORTED); } -TEST(ElementTests, ChannelElementMethods) { +TEST_F(ElementTests, ChannelElementMethods) { ElementWithChannel el1; TimeInterfaceMock time; SrpcMock srpc; diff --git a/extras/test/SuplaDeviceTests/supla_device_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_tests.cpp index 58f933e8..fe7c2211 100644 --- a/extras/test/SuplaDeviceTests/supla_device_tests.cpp +++ b/extras/test/SuplaDeviceTests/supla_device_tests.cpp @@ -26,6 +26,19 @@ using ::testing::Return; using ::testing::_; +class SuplaDeviceTests : public ::testing::Test { + protected: + virtual void SetUp() { + Supla::Channel::lastCommunicationTimeMs = 0; + memset(&(Supla::Channel::reg_dev), 0, sizeof(Supla::Channel::reg_dev)); + } + virtual void TearDown() { + Supla::Channel::lastCommunicationTimeMs = 0; + memset(&(Supla::Channel::reg_dev), 0, sizeof(Supla::Channel::reg_dev)); + } + +}; + class TimeInterfaceStub : public TimeInterface { public: virtual unsigned long millis() override { @@ -35,7 +48,7 @@ class TimeInterfaceStub : public TimeInterface { } }; -TEST(SuplaDeviceTests, DefaultValuesTest) { +TEST_F(SuplaDeviceTests, DefaultValuesTest) { SuplaDeviceClass sd; SrpcMock srpc; TimerMock timer; @@ -51,7 +64,7 @@ class ClockMock : public Supla::Clock { MOCK_METHOD(void, parseLocaltimeFromServer, (TSDC_UserLocalTimeResult *result), (override)); }; -TEST(SuplaDeviceTests, ClockMethods) { +TEST_F(SuplaDeviceTests, ClockMethods) { SuplaDeviceClass sd; ClockMock clock; @@ -66,7 +79,7 @@ TEST(SuplaDeviceTests, ClockMethods) { sd.onGetUserLocaltimeResult(nullptr); } -TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceNoElements) { +TEST_F(SuplaDeviceTests, StartWithoutNetworkInterfaceNoElements) { SuplaDeviceClass sd; TimerMock timer; @@ -88,7 +101,7 @@ class StorageMock2: public Supla::Storage { }; -TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceNoElementsWithStorage) { +TEST_F(SuplaDeviceTests, StartWithoutNetworkInterfaceNoElementsWithStorage) { ::testing::InSequence seq; SuplaDeviceClass sd; TimerMock timer; @@ -105,7 +118,7 @@ TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceNoElementsWithStorage) { EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_NETWORK_INTERFACE); } -TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceNoElementsWithStorageAndDataLoadAttempt) { +TEST_F(SuplaDeviceTests, StartWithoutNetworkInterfaceNoElementsWithStorageAndDataLoadAttempt) { ::testing::InSequence seq; SuplaDeviceClass sd; TimerMock timer; @@ -138,7 +151,7 @@ class ElementMock : public Supla::Element { }; -TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceWithElements) { +TEST_F(SuplaDeviceTests, StartWithoutNetworkInterfaceWithElements) { ::testing::InSequence seq; SuplaDeviceClass sd; TimerMock timer; @@ -156,7 +169,7 @@ TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceWithElements) { EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_NETWORK_INTERFACE); } -TEST(SuplaDeviceTests, StartWithoutNetworkInterfaceWithElementsWithStorage) { +TEST_F(SuplaDeviceTests, StartWithoutNetworkInterfaceWithElementsWithStorage) { ::testing::InSequence seq; StorageMock2 storage; SuplaDeviceClass sd; @@ -202,7 +215,7 @@ class NetworkMock : public Supla::Network { }; -TEST(SuplaDeviceTEsts, BeginStopsAtEmptyGUID) { +TEST_F(SuplaDeviceTests, BeginStopsAtEmptyGUID) { ::testing::InSequence seq; NetworkMock net; TimerMock timer; @@ -215,7 +228,7 @@ TEST(SuplaDeviceTEsts, BeginStopsAtEmptyGUID) { EXPECT_EQ(sd.getCurrentStatus(), STATUS_INVALID_GUID); } -TEST(SuplaDeviceTEsts, BeginStopsAtEmptyServer) { +TEST_F(SuplaDeviceTests, BeginStopsAtEmptyServer) { ::testing::InSequence seq; NetworkMock net; TimerMock timer; @@ -230,7 +243,7 @@ TEST(SuplaDeviceTEsts, BeginStopsAtEmptyServer) { EXPECT_EQ(sd.getCurrentStatus(), STATUS_UNKNOWN_SERVER_ADDRESS); } -TEST(SuplaDeviceTEsts, BeginStopsAtEmptyEmail) { +TEST_F(SuplaDeviceTests, BeginStopsAtEmptyEmail) { ::testing::InSequence seq; NetworkMock net; TimerMock timer; @@ -247,7 +260,7 @@ TEST(SuplaDeviceTEsts, BeginStopsAtEmptyEmail) { } -TEST(SuplaDeviceTEsts, BeginStopsAtEmptyAuthkey) { +TEST_F(SuplaDeviceTests, BeginStopsAtEmptyAuthkey) { ::testing::InSequence seq; NetworkMock net; TimerMock timer; @@ -265,7 +278,7 @@ TEST(SuplaDeviceTEsts, BeginStopsAtEmptyAuthkey) { EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_CREDENTIALS); } -TEST(SuplaDeviceTEsts, SuccessfulBegin) { +TEST_F(SuplaDeviceTests, SuccessfulBegin) { ::testing::InSequence seq; SrpcMock srpc; NetworkMock net; From 845f650355d336c5dbc0003b9d88fd23ba016220 Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 19 Feb 2021 13:44:16 +0100 Subject: [PATCH 26/38] Try some perl script instead of ts for timestamp --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index c2fdb304..507ca3b6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -7,11 +7,11 @@ steps: commands: - mkdir extras/test/build - cd extras/test/build - - cmake .. | ts '[%Y-%m-%d %H:%M:%S]' - - make -j32 | ts '[%Y-%m-%d %H:%M:%S]' + - cmake .. | perl -pe 'use POSIX strftime; $|=1; select((select(STDERR), $| = 1)[0]); print strftime "[%Y-%m-%d %H:%M:%S] ", localtime' + - make -j32 | perl -pe 'use POSIX strftime; $|=1; select((select(STDERR), $| = 1)[0]); print strftime "[%Y-%m-%d %H:%M:%S] ", localtime' - name: test image: rikorose/gcc-cmake commands: - cd extras/test/build - - ./supladevicetests --gtest_repeat=50 --gtest_shuffle | ts '[%Y-%m-%d %H:%M:%S]' + - ./supladevicetests --gtest_repeat=50 --gtest_shuffle | perl -pe 'use POSIX strftime; $|=1; select((select(STDERR), $| = 1)[0]); print strftime "[%Y-%m-%d %H:%M:%S] ", localtime' From 770f917b6e613e5cdc86b56814cd53e5da039aa7 Mon Sep 17 00:00:00 2001 From: klew Date: Mon, 22 Feb 2021 11:33:00 +0100 Subject: [PATCH 27/38] Added slack notifications to drone --- .drone.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.drone.yml b/.drone.yml index 507ca3b6..93785d40 100644 --- a/.drone.yml +++ b/.drone.yml @@ -15,3 +15,29 @@ steps: commands: - cd extras/test/build - ./supladevicetests --gtest_repeat=50 --gtest_shuffle | perl -pe 'use POSIX strftime; $|=1; select((select(STDERR), $| = 1)[0]); print strftime "[%Y-%m-%d %H:%M:%S] ", localtime' + +- name: slack + image: plugins/slack + settings: + webhook: + from_secret: slack_webhook + channel: github + username: drone + + # here's the template :) + # notice that the repo endpoint is hardcoded to `https://github.com/`. + # you may adjust it accordingly. + template: > + {{#if build.pull }} + *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}*: + {{else}} + *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}: Build #{{ build.number }}* (type: `{{ build.event }}`) + {{/if}} + + Commit: + + Branch: + + Author: {{ build.author }} + + <{{ build.link }}|Visit build page ↗> From 5deaa6cfbf95736b801581208fdd0643d76f76b1 Mon Sep 17 00:00:00 2001 From: klew Date: Mon, 22 Feb 2021 11:39:30 +0100 Subject: [PATCH 28/38] Drone slack integration template adjustment --- .drone.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.drone.yml b/.drone.yml index 93785d40..eb553806 100644 --- a/.drone.yml +++ b/.drone.yml @@ -34,10 +34,6 @@ steps: *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}: Build #{{ build.number }}* (type: `{{ build.event }}`) {{/if}} - Commit: - - Branch: - - Author: {{ build.author }} + Commit: at: by: {{ build.author }} <{{ build.link }}|Visit build page ↗> From 1e644118a85d422145b363da2105d9f1ebf0bafd Mon Sep 17 00:00:00 2001 From: klew Date: Mon, 22 Feb 2021 11:44:51 +0100 Subject: [PATCH 29/38] Fixed bug when SuplaDevice stuck when no reply to device registration was received. Additional SuplaDevice tests added --- .../supla_device_full_startup_tests.cpp | 190 ++++++++++++++++++ .../SuplaDeviceTests/supla_device_tests.cpp | 97 +++++++++ src/SuplaDevice.cpp | 54 +++-- src/SuplaDevice.h | 4 +- 4 files changed, 321 insertions(+), 24 deletions(-) create mode 100644 extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp diff --git a/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp new file mode 100644 index 00000000..7df3e12d --- /dev/null +++ b/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp @@ -0,0 +1,190 @@ +/* + 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 +#include +#include +#include +#include +#include +#include +#include + +using ::testing::Return; +using ::testing::_; +using ::testing::DoAll; +using ::testing::Assign; +using ::testing::ReturnPointee; + +class SuplaDeviceTests : public ::testing::Test { + protected: + virtual void SetUp() { + Supla::Channel::lastCommunicationTimeMs = 0; + memset(&(Supla::Channel::reg_dev), 0, sizeof(Supla::Channel::reg_dev)); + } + virtual void TearDown() { + Supla::Channel::lastCommunicationTimeMs = 0; + memset(&(Supla::Channel::reg_dev), 0, sizeof(Supla::Channel::reg_dev)); + } + +}; + +class TimeInterfaceStub : public TimeInterface { + public: + virtual unsigned long millis() override { + static unsigned long value = 0; + value += 1000; + return value; + } +}; + +class StorageMock2: public Supla::Storage { + public: + MOCK_METHOD(bool, init, (), (override)); + MOCK_METHOD(bool, prepareState, (bool), (override)); + MOCK_METHOD(bool, finalizeSaveState, (), (override)); + MOCK_METHOD(void, commit, (), (override)); + MOCK_METHOD(int, readStorage, (unsigned int, unsigned char *, int, bool), (override)); + MOCK_METHOD(int, writeStorage, (unsigned int, const unsigned char *, int), (override)); + +}; + +class ElementMock : public Supla::Element { + public: + MOCK_METHOD(void, onInit, (), (override)); + MOCK_METHOD(void, onLoadState, (), (override)); + MOCK_METHOD(void, onSaveState, (), (override)); + MOCK_METHOD(void, iterateAlways, (), (override)); + MOCK_METHOD(bool, iterateConnected, (void *), (override)); + MOCK_METHOD(void, onTimer, (), (override)); + MOCK_METHOD(void, onFastTimer, (), (override)); + MOCK_METHOD(int, handleNewValueFromServer, (TSD_SuplaChannelNewValue *), (override)); + MOCK_METHOD(void, handleGetChannelState, (TDSC_ChannelState &), (override)); + MOCK_METHOD(int, handleCalcfgFromServer, (TSD_DeviceCalCfgRequest *), (override)); + +}; + +class NetworkMock : public Supla::Network { + public: + NetworkMock() : Supla::Network(nullptr) {}; + MOCK_METHOD(int, read, (void *, int ), (override)); + MOCK_METHOD(int, write, (void *, int ), (override)); + MOCK_METHOD(int, connect, (const char *, int), (override)); + MOCK_METHOD(bool, connected, (), (override)); + MOCK_METHOD(void, disconnect, (), (override)); + MOCK_METHOD(void, setup, (), (override)); + MOCK_METHOD(void, setTimeout, (int), (override)); + + MOCK_METHOD(bool, isReady, (), (override)); + MOCK_METHOD(bool, iterate, (), (override)); + MOCK_METHOD(bool, ping, (void *), (override)); + +}; + +class SuplaDeviceTestsFullStartup : public SuplaDeviceTests { + protected: + SrpcMock srpc; + NetworkMock net; + TimerMock timer; + TimeInterfaceStub time; + SuplaDeviceClass sd; + ElementMock el1; + ElementMock el2; + + virtual void SetUp() { + SuplaDeviceTests::SetUp(); + + int dummy; + + EXPECT_CALL(el1, onInit()); + EXPECT_CALL(el2, onInit()); + + EXPECT_CALL(timer, initTimers()); + EXPECT_CALL(net, setup()); + EXPECT_CALL(srpc, srpc_params_init(_)); + EXPECT_CALL(srpc, srpc_init(_)).WillOnce(Return(&dummy)); + EXPECT_CALL(srpc, srpc_set_proto_version(&dummy, 12)); + + char GUID[SUPLA_GUID_SIZE] = {1}; + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {2}; + EXPECT_TRUE(sd.begin(GUID, "supla.rulez", "superman@supla.org", AUTHKEY)); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_INITIALIZED); + } + + virtual void TearDown() { + SuplaDeviceTests::TearDown(); + } +}; + +using ::testing::AtLeast; + +TEST_F(SuplaDeviceTestsFullStartup, NoNetworkShouldCallSetupAgain) { + EXPECT_CALL(net, isReady()).WillRepeatedly(Return(false)); + EXPECT_CALL(net, setup()).Times(2); + EXPECT_CALL(el1, iterateAlways()).Times(AtLeast(1)); + EXPECT_CALL(el2, iterateAlways()).Times(AtLeast(1)); + + for (int i = 0; i < 50*30; i++) sd.iterate(); +} + +TEST_F(SuplaDeviceTestsFullStartup, FailedConnectionShouldSetupNetworkAgain) { + EXPECT_CALL(net, isReady()).WillRepeatedly(Return(true)); + EXPECT_CALL(net, connected()).WillRepeatedly(Return(false)); + EXPECT_CALL(net, connect(_, _)).WillRepeatedly(Return(0)); + EXPECT_CALL(net, disconnect()).Times(AtLeast(1)); + + EXPECT_CALL(net, setup()).Times(1); + EXPECT_CALL(el1, iterateAlways()).Times(AtLeast(1)); + EXPECT_CALL(el2, iterateAlways()).Times(AtLeast(1)); + + for (int i = 0; i < 2*31; i++) sd.iterate(); +} + +TEST_F(SuplaDeviceTestsFullStartup, SrpcFailureShouldCallDisconnect) { + EXPECT_CALL(net, isReady()).WillRepeatedly(Return(true)); + EXPECT_CALL(net, connected()).WillOnce(Return(false)).WillRepeatedly(Return(false)); + EXPECT_CALL(net, connect(_, _)).WillRepeatedly(Return(1)); + EXPECT_CALL(net, iterate()).Times(1); + EXPECT_CALL(srpc, srpc_iterate(_)).WillOnce(Return(SUPLA_RESULT_FALSE)); + + EXPECT_CALL(net, disconnect()).Times(1); + + EXPECT_CALL(el1, iterateAlways()).Times(AtLeast(1)); + EXPECT_CALL(el2, iterateAlways()).Times(AtLeast(1)); + + sd.iterate(); +} + +TEST_F(SuplaDeviceTestsFullStartup, NoReplyForDeviceRegistrationShoudResetConnection) { + bool isConnected = false; + EXPECT_CALL(net, isReady()).WillRepeatedly(Return(true)); + EXPECT_CALL(net, connected()).WillRepeatedly(ReturnPointee(&isConnected)); + EXPECT_CALL(net, connect(_, _)).WillRepeatedly(DoAll(Assign(&isConnected, true), Return(1))); + + EXPECT_CALL(net, iterate()).Times(AtLeast(1)); + EXPECT_CALL(srpc, srpc_iterate(_)).WillRepeatedly(Return(SUPLA_RESULT_TRUE)); + + EXPECT_CALL(el1, iterateAlways()).Times(AtLeast(1)); + EXPECT_CALL(el2, iterateAlways()).Times(AtLeast(1)); + + EXPECT_CALL(net, disconnect()).WillOnce(Assign(&isConnected, false)); + + EXPECT_CALL(srpc, srpc_ds_async_registerdevice_e(_, _)).Times(2); + + for (int i = 0; i < 15; i++) sd.iterate(); +} + + diff --git a/extras/test/SuplaDeviceTests/supla_device_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_tests.cpp index fe7c2211..09be0c09 100644 --- a/extras/test/SuplaDeviceTests/supla_device_tests.cpp +++ b/extras/test/SuplaDeviceTests/supla_device_tests.cpp @@ -25,6 +25,9 @@ using ::testing::Return; using ::testing::_; +using ::testing::DoAll; +using ::testing::Assign; +using ::testing::ReturnPointee; class SuplaDeviceTests : public ::testing::Test { protected: @@ -164,9 +167,17 @@ TEST_F(SuplaDeviceTests, StartWithoutNetworkInterfaceWithElements) { EXPECT_CALL(el2, onInit()); EXPECT_CALL(timer, initTimers()); + EXPECT_CALL(el1, onTimer()); + EXPECT_CALL(el2, onTimer()); + EXPECT_CALL(el1, onFastTimer()); + EXPECT_CALL(el2, onFastTimer()); + + EXPECT_FALSE(sd.begin()); EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_NETWORK_INTERFACE); + sd.onTimer(); + sd.onFastTimer(); } TEST_F(SuplaDeviceTests, StartWithoutNetworkInterfaceWithElementsWithStorage) { @@ -303,3 +314,89 @@ TEST_F(SuplaDeviceTests, SuccessfulBegin) { EXPECT_EQ(sd.getCurrentStatus(), STATUS_INITIALIZED); } + + +TEST_F(SuplaDeviceTests, SuccessfulBeginAlternative) { + ::testing::InSequence seq; + SrpcMock srpc; + NetworkMock net; + TimerMock timer; + + SuplaDeviceClass sd; + int dummy; + + EXPECT_CALL(timer, initTimers()); + EXPECT_CALL(net, setup()); + EXPECT_CALL(srpc, srpc_params_init(_)); + EXPECT_CALL(srpc, srpc_init(_)).WillOnce(Return(&dummy)); + EXPECT_CALL(srpc, srpc_set_proto_version(&dummy, 12)); + + char GUID[SUPLA_GUID_SIZE] = {1}; + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {2}; + EXPECT_TRUE(sd.begin(GUID, "supla.rulez", "superman@supla.org", AUTHKEY)); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_INITIALIZED); +} + + +TEST_F(SuplaDeviceTests, FailedBeginAlternativeOnEmptyAUTHKEY) { + ::testing::InSequence seq; + SrpcMock srpc; + NetworkMock net; + TimerMock timer; + + SuplaDeviceClass sd; + int dummy; + + EXPECT_CALL(timer, initTimers()); + + char GUID[SUPLA_GUID_SIZE] = {1}; + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0}; + EXPECT_FALSE(sd.begin(GUID, "supla.rulez", "superman@supla.org", AUTHKEY)); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_MISSING_CREDENTIALS); +} + +TEST_F(SuplaDeviceTests, TwoChannelElementsNoNetworkWithStorage) { + SrpcMock srpc; + NetworkMock net; + StorageMock2 storage; + TimerMock timer; + TimeInterfaceStub time; + SuplaDeviceClass sd; + ElementMock el1; + ElementMock el2; + int dummy; + EXPECT_CALL(storage, prepareState(true)).WillOnce(Return(true)); + EXPECT_CALL(storage, init()); + EXPECT_CALL(el1, onSaveState()); + EXPECT_CALL(el2, onSaveState()); + + EXPECT_CALL(storage, finalizeSaveState()).WillOnce(Return(true)); + EXPECT_CALL(storage, prepareState(false)); + EXPECT_CALL(el1, onLoadState()); + EXPECT_CALL(el2, onLoadState()); + + EXPECT_CALL(el1, onInit()); + EXPECT_CALL(el2, onInit()); + + EXPECT_CALL(timer, initTimers()); + EXPECT_CALL(net, setup()); + EXPECT_CALL(srpc, srpc_params_init(_)); + EXPECT_CALL(srpc, srpc_init(_)).WillOnce(Return(&dummy)); + EXPECT_CALL(srpc, srpc_set_proto_version(&dummy, 12)); + + char GUID[SUPLA_GUID_SIZE] = {1}; + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {2}; + EXPECT_TRUE(sd.begin(GUID, "supla.rulez", "superman@supla.org", AUTHKEY)); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_INITIALIZED); + EXPECT_CALL(el1, iterateAlways()).Times(2); + EXPECT_CALL(el2, iterateAlways()).Times(2); + EXPECT_CALL(net, isReady()).WillRepeatedly(Return(false)); + + EXPECT_CALL(storage, prepareState(false)); + EXPECT_CALL(el1, onSaveState()); + EXPECT_CALL(el2, onSaveState()); + EXPECT_CALL(storage, finalizeSaveState()); + + for (int i = 0; i < 2; i++) sd.iterate(); +} + diff --git a/src/SuplaDevice.cpp b/src/SuplaDevice.cpp index eeb00a5b..465d0b04 100644 --- a/src/SuplaDevice.cpp +++ b/src/SuplaDevice.cpp @@ -15,7 +15,6 @@ */ #include - #include #include "SuplaDevice.h" @@ -48,8 +47,8 @@ SuplaDeviceClass::SuplaDeviceClass() impl_arduino_status(nullptr) { srpc = NULL; registered = 0; - last_iterate_time = 0; - wait_for_iterate = 0; + lastIterateTime = 0; + waitForIterate = 0; } SuplaDeviceClass::~SuplaDeviceClass() { @@ -96,10 +95,10 @@ bool SuplaDeviceClass::begin(unsigned char version) { // Pefrorm dry run of write state to validate stored state section with // current device configuration if (Supla::Storage::PrepareState(true)) { - Serial.println( - F("Validating storage state section with current device configuration")); + Serial.println(F( + "Validating storage state section with current device configuration")); for (auto element = Supla::Element::begin(); element != nullptr; - element = element->next()) { + element = element->next()) { element->onSaveState(); delay(0); } @@ -111,7 +110,7 @@ bool SuplaDeviceClass::begin(unsigned char version) { // Iterate all elements and load state Supla::Storage::PrepareState(); for (auto element = Supla::Element::begin(); element != nullptr; - element = element->next()) { + element = element->next()) { element->onLoadState(); delay(0); } @@ -122,7 +121,7 @@ bool SuplaDeviceClass::begin(unsigned char version) { // Initialize elements for (auto element = Supla::Element::begin(); element != nullptr; - element = element->next()) { + element = element->next()) { element->onInit(); delay(0); } @@ -182,8 +181,8 @@ bool SuplaDeviceClass::begin(unsigned char version) { if (strnlen(Supla::Channel::reg_dev.SoftVer, SUPLA_SOFTVER_MAXSIZE) == 0) { setString(Supla::Channel::reg_dev.SoftVer, - "User SW, lib 2.3.3", - SUPLA_SOFTVER_MAXSIZE); + "User SW, lib 2.3.3", + SUPLA_SOFTVER_MAXSIZE); } Serial.println(F("Initializing network layer")); @@ -247,7 +246,7 @@ void SuplaDeviceClass::iterate(void) { if (!isInitialized(false)) return; unsigned long _millis = millis(); - unsigned long time_diff = abs(_millis - last_iterate_time); + unsigned long timeDiff = abs(_millis - lastIterateTime); uptime.iterate(_millis); @@ -269,11 +268,10 @@ void SuplaDeviceClass::iterate(void) { Supla::Storage::FinalizeSaveState(); } - if (wait_for_iterate != 0 && _millis < wait_for_iterate) { + if (waitForIterate != 0 && _millis < waitForIterate) { return; - } else { - wait_for_iterate = 0; + waitForIterate = 0; } // Restart network after >1 min of failed connection attempts @@ -289,7 +287,7 @@ void SuplaDeviceClass::iterate(void) { if (!Supla::Network::IsReady()) { uptime.setConnectionLostCause( SUPLA_LASTCONNECTIONRESETCAUSE_WIFI_CONNECTION_LOST); - wait_for_iterate = millis() + 100; + waitForIterate = _millis + 100; status(STATUS_NETWORK_DISCONNECTED, "No connection to network"); networkIsNotReadyCounter++; if (networkIsNotReadyCounter > 20) { @@ -321,7 +319,7 @@ void SuplaDeviceClass::iterate(void) { Supla::Channel::reg_dev.ServerName); Supla::Network::Disconnect(); - wait_for_iterate = millis() + 2000; + waitForIterate = _millis + 2000; connectionFailCounter++; return; } @@ -333,18 +331,30 @@ void SuplaDeviceClass::iterate(void) { status(STATUS_ITERATE_FAIL, "Iterate fail"); Supla::Network::Disconnect(); - wait_for_iterate = millis() + 5000; + waitForIterate = _millis + 5000; return; } if (registered == 0) { + // Perform registration if we are not yet registered registered = -1; + lastIterateTime = _millis; status(STATUS_REGISTER_IN_PROGRESS, "Register in progress"); if (!srpc_ds_async_registerdevice_e(srpc, &Supla::Channel::reg_dev)) { supla_log(LOG_DEBUG, "Fatal SRPC failure!"); } + } else if (registered == -1) { + // Handle registration timeout (in case of no reply received) + if (timeDiff > 10*1000) { + supla_log(LOG_DEBUG, "No reply to registration message. Resetting connection."); + Supla::Network::Disconnect(); + + waitForIterate = _millis + 2000; + connectionFailCounter++; + } } else if (registered == 1) { + // Device is registered and everything is correct if (Supla::Network::Ping(srpc) == false) { uptime.setConnectionLostCause( SUPLA_LASTCONNECTIONRESETCAUSE_ACTIVITY_TIMEOUT); @@ -352,7 +362,7 @@ void SuplaDeviceClass::iterate(void) { Supla::Network::Disconnect(); } - if (time_diff > 0) { + if (timeDiff > 0) { // Iterate all elements for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { @@ -362,7 +372,7 @@ void SuplaDeviceClass::iterate(void) { delay(0); } - last_iterate_time = millis(); + lastIterateTime = _millis; } } } @@ -376,7 +386,7 @@ void SuplaDeviceClass::onVersionError(TSDC_SuplaVersionError *version_error) { Supla::Network::Disconnect(); - wait_for_iterate = millis() + 5000; + waitForIterate = millis() + 5000; } void SuplaDeviceClass::onRegisterResult( @@ -395,7 +405,7 @@ void SuplaDeviceClass::onRegisterResult( register_device_result->activity_timeout, register_device_result->version, register_device_result->version_min); - last_iterate_time = millis(); + lastIterateTime = millis(); status(STATUS_REGISTERED_AND_READY, "Registered and ready."); if (activity_timeout != ACTIVITY_TIMEOUT) { @@ -464,7 +474,7 @@ void SuplaDeviceClass::onRegisterResult( } Supla::Network::Disconnect(); - wait_for_iterate = millis() + 5000; + waitForIterate = millis() + 5000; } void SuplaDeviceClass::channelSetActivityTimeoutResult( diff --git a/src/SuplaDevice.h b/src/SuplaDevice.h index 0057105c..17dbb786 100644 --- a/src/SuplaDevice.h +++ b/src/SuplaDevice.h @@ -58,8 +58,8 @@ class SuplaDeviceClass { int connectionFailCounter; int networkIsNotReadyCounter; - unsigned long last_iterate_time; - unsigned long wait_for_iterate; + unsigned long lastIterateTime; + unsigned long waitForIterate; _impl_arduino_status impl_arduino_status; int currentStatus; From 3562c37503f2a6086255d4e1c10d30d5a66b2dd7 Mon Sep 17 00:00:00 2001 From: klew Date: Mon, 22 Feb 2021 11:46:17 +0100 Subject: [PATCH 30/38] Failed test - drone check --- .../test/SuplaDeviceTests/supla_device_full_startup_tests.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp index 7df3e12d..fe5c0213 100644 --- a/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp +++ b/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp @@ -187,4 +187,7 @@ TEST_F(SuplaDeviceTestsFullStartup, NoReplyForDeviceRegistrationShoudResetConnec for (int i = 0; i < 15; i++) sd.iterate(); } +TEST(SuperTest, TestEverything) { + ASSERT_TRUE(false); +} From 0be0d09de975667f26a5d434fe2d4d26480fdcc1 Mon Sep 17 00:00:00 2001 From: klew Date: Mon, 22 Feb 2021 11:56:33 +0100 Subject: [PATCH 31/38] Removed timestamps from drone --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index eb553806..a71a679e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -7,14 +7,14 @@ steps: commands: - mkdir extras/test/build - cd extras/test/build - - cmake .. | perl -pe 'use POSIX strftime; $|=1; select((select(STDERR), $| = 1)[0]); print strftime "[%Y-%m-%d %H:%M:%S] ", localtime' - - make -j32 | perl -pe 'use POSIX strftime; $|=1; select((select(STDERR), $| = 1)[0]); print strftime "[%Y-%m-%d %H:%M:%S] ", localtime' + - cmake .. + - make -j32 - name: test image: rikorose/gcc-cmake commands: - cd extras/test/build - - ./supladevicetests --gtest_repeat=50 --gtest_shuffle | perl -pe 'use POSIX strftime; $|=1; select((select(STDERR), $| = 1)[0]); print strftime "[%Y-%m-%d %H:%M:%S] ", localtime' + - ./supladevicetests --gtest_repeat=50 --gtest_shuffle - name: slack image: plugins/slack From 87a5a1b6a45084a5c70380d9d237d840cc305fa9 Mon Sep 17 00:00:00 2001 From: klew Date: Mon, 22 Feb 2021 12:02:28 +0100 Subject: [PATCH 32/38] Enabled slack notifications on build failure --- .drone.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.drone.yml b/.drone.yml index a71a679e..f3dcad12 100644 --- a/.drone.yml +++ b/.drone.yml @@ -37,3 +37,5 @@ steps: Commit: at: by: {{ build.author }} <{{ build.link }}|Visit build page ↗> + when: [success, failure] + From ed3c80d8731f3872d00a19bb5f376160db3df8bf Mon Sep 17 00:00:00 2001 From: klew Date: Mon, 22 Feb 2021 12:04:13 +0100 Subject: [PATCH 33/38] Fix drone --- .drone.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index f3dcad12..1419359c 100644 --- a/.drone.yml +++ b/.drone.yml @@ -37,5 +37,6 @@ steps: Commit: at: by: {{ build.author }} <{{ build.link }}|Visit build page ↗> - when: [success, failure] + when: + status: [success, failure] From fca93f93abc98c6576c9b46ae76888bf978e8c5f Mon Sep 17 00:00:00 2001 From: klew Date: Mon, 22 Feb 2021 12:18:02 +0100 Subject: [PATCH 34/38] Removed failing test --- .../test/SuplaDeviceTests/supla_device_full_startup_tests.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp index fe5c0213..594e6df5 100644 --- a/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp +++ b/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp @@ -187,7 +187,3 @@ TEST_F(SuplaDeviceTestsFullStartup, NoReplyForDeviceRegistrationShoudResetConnec for (int i = 0; i < 15; i++) sd.iterate(); } -TEST(SuperTest, TestEverything) { - - ASSERT_TRUE(false); -} From 102f1941eaac90b316b20391f9213bba5c7ae380 Mon Sep 17 00:00:00 2001 From: klew Date: Tue, 23 Feb 2021 14:30:49 +0100 Subject: [PATCH 35/38] SuplaDevice - successful startup test --- .../supla_device_full_startup_tests.cpp | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp index 594e6df5..44e2bbbb 100644 --- a/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp +++ b/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp @@ -187,3 +187,38 @@ TEST_F(SuplaDeviceTestsFullStartup, NoReplyForDeviceRegistrationShoudResetConnec for (int i = 0; i < 15; i++) sd.iterate(); } + +TEST_F(SuplaDeviceTestsFullStartup, SuccessfulStartup) { + bool isConnected = false; + EXPECT_CALL(net, isReady()).WillRepeatedly(Return(true)); + EXPECT_CALL(net, connected()).WillRepeatedly(ReturnPointee(&isConnected)); + EXPECT_CALL(net, connect(_, _)).WillRepeatedly(DoAll(Assign(&isConnected, true), Return(1))); + + EXPECT_CALL(net, iterate()).Times(AtLeast(1)); + EXPECT_CALL(srpc, srpc_iterate(_)).WillRepeatedly(Return(SUPLA_RESULT_TRUE)); + + EXPECT_CALL(el1, iterateAlways()).Times(35); + EXPECT_CALL(el2, iterateAlways()).Times(35); + + EXPECT_CALL(srpc, srpc_ds_async_registerdevice_e(_, _)).Times(1); + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(1); + + EXPECT_CALL(net, ping(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(el1, iterateConnected(_)).Times(30).WillRepeatedly(Return(true)); + EXPECT_CALL(el2, iterateConnected(_)).Times(30).WillRepeatedly(Return(true)); + + for (int i = 0; i < 5; i++) sd.iterate(); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_TRUE; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + for (int i = 0; i < 30; i++) sd.iterate(); + + +} + From 4dee3abcd8c29b04ac960530dc4538a85cb9be9e Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 26 Feb 2021 11:46:09 +0100 Subject: [PATCH 36/38] SuplaDevice tests --- .../supla_device_full_startup_tests.cpp | 14 +- .../SuplaDeviceTests/supla_device_tests.cpp | 293 +++++++++++++++++- src/SuplaDevice.cpp | 5 +- src/SuplaDevice.h | 1 + 4 files changed, 308 insertions(+), 5 deletions(-) diff --git a/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp index 44e2bbbb..3024574e 100644 --- a/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp +++ b/extras/test/SuplaDeviceTests/supla_device_full_startup_tests.cpp @@ -138,6 +138,7 @@ TEST_F(SuplaDeviceTestsFullStartup, NoNetworkShouldCallSetupAgain) { EXPECT_CALL(el2, iterateAlways()).Times(AtLeast(1)); for (int i = 0; i < 50*30; i++) sd.iterate(); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_NETWORK_DISCONNECTED); } TEST_F(SuplaDeviceTestsFullStartup, FailedConnectionShouldSetupNetworkAgain) { @@ -151,6 +152,7 @@ TEST_F(SuplaDeviceTestsFullStartup, FailedConnectionShouldSetupNetworkAgain) { EXPECT_CALL(el2, iterateAlways()).Times(AtLeast(1)); for (int i = 0; i < 2*31; i++) sd.iterate(); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_SERVER_DISCONNECTED); } TEST_F(SuplaDeviceTestsFullStartup, SrpcFailureShouldCallDisconnect) { @@ -166,6 +168,7 @@ TEST_F(SuplaDeviceTestsFullStartup, SrpcFailureShouldCallDisconnect) { EXPECT_CALL(el2, iterateAlways()).Times(AtLeast(1)); sd.iterate(); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_ITERATE_FAIL); } TEST_F(SuplaDeviceTestsFullStartup, NoReplyForDeviceRegistrationShoudResetConnection) { @@ -184,7 +187,10 @@ TEST_F(SuplaDeviceTestsFullStartup, NoReplyForDeviceRegistrationShoudResetConnec EXPECT_CALL(srpc, srpc_ds_async_registerdevice_e(_, _)).Times(2); - for (int i = 0; i < 15; i++) sd.iterate(); + for (int i = 0; i < 11; i++) sd.iterate(); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_SERVER_DISCONNECTED); + for (int i = 0; i < 2; i++) sd.iterate(); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_REGISTER_IN_PROGRESS); } @@ -207,7 +213,9 @@ TEST_F(SuplaDeviceTestsFullStartup, SuccessfulStartup) { EXPECT_CALL(el1, iterateConnected(_)).Times(30).WillRepeatedly(Return(true)); EXPECT_CALL(el2, iterateConnected(_)).Times(30).WillRepeatedly(Return(true)); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_INITIALIZED); for (int i = 0; i < 5; i++) sd.iterate(); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_REGISTER_IN_PROGRESS); TSD_SuplaRegisterDeviceResult register_device_result{}; register_device_result.result_code = SUPLA_RESULTCODE_TRUE; @@ -217,8 +225,10 @@ TEST_F(SuplaDeviceTestsFullStartup, SuccessfulStartup) { sd.onRegisterResult(®ister_device_result); - for (int i = 0; i < 30; i++) sd.iterate(); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_REGISTERED_AND_READY); + for (int i = 0; i < 30; i++) sd.iterate(); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_REGISTERED_AND_READY); } diff --git a/extras/test/SuplaDeviceTests/supla_device_tests.cpp b/extras/test/SuplaDeviceTests/supla_device_tests.cpp index 09be0c09..e6cf3ed0 100644 --- a/extras/test/SuplaDeviceTests/supla_device_tests.cpp +++ b/extras/test/SuplaDeviceTests/supla_device_tests.cpp @@ -58,8 +58,6 @@ TEST_F(SuplaDeviceTests, DefaultValuesTest) { EXPECT_EQ(sd.getCurrentStatus(), STATUS_UNKNOWN); EXPECT_EQ(sd.getClock(), nullptr); - - } class ClockMock : public Supla::Clock { @@ -400,3 +398,294 @@ TEST_F(SuplaDeviceTests, TwoChannelElementsNoNetworkWithStorage) { for (int i = 0; i < 2; i++) sd.iterate(); } +TEST_F(SuplaDeviceTests, OnVersionErrorShouldCallDisconnect) { + NetworkMock net; + TimeInterfaceStub time; + + EXPECT_CALL(net, disconnect()).Times(1); + + SuplaDeviceClass sd; + TSDC_SuplaVersionError versionError{}; + + sd.onVersionError(&versionError); + EXPECT_EQ(sd.getCurrentStatus(), STATUS_PROTOCOL_VERSION_ERROR); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultOK) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_TRUE; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_REGISTERED_AND_READY); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultBadCredentials) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_BAD_CREDENTIALS; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_BAD_CREDENTIALS); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultTemporairlyUnavailable) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_TEMPORARILY_UNAVAILABLE; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_TEMPORARILY_UNAVAILABLE); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultLocationConflict) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_LOCATION_CONFLICT; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_LOCATION_CONFLICT); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultChannelConflict) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_CHANNEL_CONFLICT; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_CHANNEL_CONFLICT); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultDeviceDisabled) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_DEVICE_DISABLED; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_DEVICE_IS_DISABLED); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultLocationDisabled) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_LOCATION_DISABLED; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_LOCATION_IS_DISABLED); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultDeviceLimitExceeded) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_DEVICE_LIMITEXCEEDED; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_DEVICE_LIMIT_EXCEEDED); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultGuidError) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_GUID_ERROR; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_INVALID_GUID); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultAuthKeyError) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_AUTHKEY_ERROR; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_INVALID_GUID); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultRegistrationDisabled) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_REGISTRATION_DISABLED; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_REGISTRATION_DISABLED); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultNoLocationAvailable) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_NO_LOCATION_AVAILABLE; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_INVALID_GUID); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultUserConflict) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = SUPLA_RESULTCODE_USER_CONFLICT; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_INVALID_GUID); +} + +TEST_F(SuplaDeviceTests, OnRegisterResultUnknownError) { + NetworkMock net; + SrpcMock srpc; + TimeInterfaceStub time; + SuplaDeviceClass sd; + + EXPECT_CALL(srpc, srpc_dcs_async_set_activity_timeout(_, _)).Times(0); + EXPECT_CALL(net, disconnect()).Times(1); + + TSD_SuplaRegisterDeviceResult register_device_result{}; + register_device_result.result_code = 666; + register_device_result.activity_timeout = 45; + register_device_result.version = 12; + register_device_result.version_min = 1; + + sd.onRegisterResult(®ister_device_result); + + EXPECT_EQ(sd.getCurrentStatus(), STATUS_UNKNOWN_ERROR); +} diff --git a/src/SuplaDevice.cpp b/src/SuplaDevice.cpp index 465d0b04..53d139cf 100644 --- a/src/SuplaDevice.cpp +++ b/src/SuplaDevice.cpp @@ -347,6 +347,7 @@ void SuplaDeviceClass::iterate(void) { // Handle registration timeout (in case of no reply received) if (timeDiff > 10*1000) { supla_log(LOG_DEBUG, "No reply to registration message. Resetting connection."); + status(STATUS_SERVER_DISCONNECTED, "Not connected to Supla server"); Supla::Network::Disconnect(); waitForIterate = _millis + 2000; @@ -359,6 +360,7 @@ void SuplaDeviceClass::iterate(void) { uptime.setConnectionLostCause( SUPLA_LASTCONNECTIONRESETCAUSE_ACTIVITY_TIMEOUT); supla_log(LOG_DEBUG, "TIMEOUT - lost connection with server"); + status(STATUS_SERVER_DISCONNECTED, "Not connected to Supla server"); Supla::Network::Disconnect(); } @@ -455,7 +457,7 @@ void SuplaDeviceClass::onRegisterResult( break; case SUPLA_RESULTCODE_REGISTRATION_DISABLED: - status(STATUS_INVALID_GUID, "Registration disabled!"); + status(STATUS_REGISTRATION_DISABLED, "Registration disabled!"); break; case SUPLA_RESULTCODE_NO_LOCATION_AVAILABLE: @@ -467,6 +469,7 @@ void SuplaDeviceClass::onRegisterResult( break; default: + status(STATUS_UNKNOWN_ERROR, "Unknown registration error"); supla_log(LOG_ERR, "Register result code %i", register_device_result->result_code); diff --git a/src/SuplaDevice.h b/src/SuplaDevice.h index 17dbb786..b557b4d4 100644 --- a/src/SuplaDevice.h +++ b/src/SuplaDevice.h @@ -47,6 +47,7 @@ #define STATUS_NETWORK_DISCONNECTED 21 #define STATUS_REGISTRATION_DISABLED 22 #define STATUS_MISSING_CREDENTIALS 23 +#define STATUS_UNKNOWN_ERROR 24 typedef void (*_impl_arduino_status)(int status, const char *msg); From 95b2244e450f59b7e3480784a49a575cadd9d0d6 Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 26 Feb 2021 11:51:16 +0100 Subject: [PATCH 37/38] Added drone build duration --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 1419359c..c5ba10d3 100644 --- a/.drone.yml +++ b/.drone.yml @@ -29,9 +29,9 @@ steps: # you may adjust it accordingly. template: > {{#if build.pull }} - *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}*: + *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}*: in {{since build.started}} {{else}} - *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}: Build #{{ build.number }}* (type: `{{ build.event }}`) + *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}: Build #{{ build.number }}* (type: `{{ build.event }}`) in {{since build.started}} {{/if}} Commit: at: by: {{ build.author }} From 3541c2f1591b4c78fd98681a88361a2b012c5dc4 Mon Sep 17 00:00:00 2001 From: klew Date: Fri, 26 Feb 2021 11:54:15 +0100 Subject: [PATCH 38/38] Drone: time --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index c5ba10d3..98d2446f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -29,9 +29,9 @@ steps: # you may adjust it accordingly. template: > {{#if build.pull }} - *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}*: in {{since build.started}} + *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}*: in {{since build.created}} {{else}} - *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}: Build #{{ build.number }}* (type: `{{ build.event }}`) in {{since build.started}} + *{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}: Build #{{ build.number }}* (type: `{{ build.event }}`) in {{since build.created}} {{/if}} Commit: at: by: {{ build.author }}