supla-device
Loading...
Searching...
No Matches
custom_parameter.h
1// SPDX-FileCopyrightText: AC SOFTWARE SP. Z O.O.
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#ifndef SRC_SUPLA_NETWORK_HTML_CUSTOM_PARAMETER_H_
5#define SRC_SUPLA_NETWORK_HTML_CUSTOM_PARAMETER_H_
6
7#ifndef ARDUINO_ARCH_AVR
8
9#include <inttypes.h>
10#include <stdint.h>
11#include <stdio.h>
12#include <string.h>
13#include <supla/network/html_element.h>
14#include <supla/network/web_sender.h>
15#include <supla/storage/config.h>
16#include <supla/storage/storage.h>
17#include <supla/tools.h>
18
19#include <limits>
20#include <type_traits>
21
22namespace Supla {
23
24namespace Html {
25
31template <typename T>
33 public:
34 CustomParameterTemplate(const char* paramTag,
35 const char* paramLabel,
36 T defaultValue = static_cast<T>(0),
37 T minValue = std::numeric_limits<T>::lowest(),
38 T maxValue = std::numeric_limits<T>::max(),
39 uint8_t decimalPlaces = 0);
40
42
43 void send(Supla::WebSender* sender) override;
44 bool handleResponse(const char* key, const char* value) override;
45
46 T getParameterValue();
47 void setParameterValue(T newValue);
48
49 protected:
50 static_assert(
51 (std::is_integral<T>::value && std::is_signed<T>::value) ||
52 std::is_floating_point<T>::value,
53 "CustomParameterTemplate supports signed integers and floating types");
54
55 int32_t parameterValue = 0;
56 char* tag = nullptr;
57 char* label = nullptr;
58 int32_t minStoredValue = 0;
59 int32_t maxStoredValue = 0;
60 uint8_t decimalPlaces = 0;
61
62 static uint8_t sanitizeDecimalPlaces(uint8_t requestedDecimalPlaces);
63 static int32_t getScaleFactor(uint8_t decimalPlaces);
64 static int32_t clampToInt32(long double value);
65
66 int32_t encodeValue(T value) const;
67 T decodeValue(int32_t value) const;
68 void setStoredValue(int32_t newValue, bool saveToConfig);
69};
70
71template <typename T>
73 const char* paramLabel,
74 T defaultValue,
75 T minValue,
76 T maxValue,
77 uint8_t decimalPlaces)
78: HtmlElement(HTML_SECTION_FORM),
79decimalPlaces(sanitizeDecimalPlaces(decimalPlaces)) {
80 if (paramTag != nullptr) {
81 int size = strlen(paramTag);
82 if (size < 500) {
83 tag = new char[size + 1];
84 strncpy(tag, paramTag, size + 1);
85 }
86 }
87
88 if (paramLabel != nullptr) {
89 int size = strlen(paramLabel);
90 if (size < 500) {
91 label = new char[size + 1];
92 strncpy(label, paramLabel, size + 1);
93 }
94 }
95
96 minStoredValue = encodeValue(minValue);
97 maxStoredValue = encodeValue(maxValue);
98 if (minStoredValue > maxStoredValue) {
99 const int32_t tmp = minStoredValue;
100 minStoredValue = maxStoredValue;
101 maxStoredValue = tmp;
102 }
103
104 setStoredValue(encodeValue(defaultValue), false);
105}
106
107template <typename T>
108CustomParameterTemplate<T>::~CustomParameterTemplate() {
109 if (tag != nullptr) {
110 delete[] tag;
111 tag = nullptr;
112 }
113 if (label != nullptr) {
114 delete[] label;
115 label = nullptr;
116 }
117}
118
119template <typename T>
120uint8_t CustomParameterTemplate<T>::sanitizeDecimalPlaces(
121 uint8_t requestedDecimalPlaces) {
122 if (std::is_integral<T>::value) {
123 return 0;
124 }
125 return requestedDecimalPlaces > 6 ? 6 : requestedDecimalPlaces;
126}
127
128template <typename T>
129int32_t CustomParameterTemplate<T>::getScaleFactor(uint8_t decimalPlaces) {
130 int32_t factor = 1;
131 for (uint8_t i = 0; i < decimalPlaces; i++) {
132 factor *= 10;
133 }
134 return factor;
135}
136
137template <typename T>
138int32_t CustomParameterTemplate<T>::clampToInt32(long double value) {
139 if (value > static_cast<long double>(INT32_MAX)) {
140 return INT32_MAX;
141 }
142 if (value < static_cast<long double>(INT32_MIN)) {
143 return INT32_MIN;
144 }
145 return static_cast<int32_t>(value);
146}
147
148template <typename T>
149int32_t CustomParameterTemplate<T>::encodeValue(T value) const {
150 if (std::is_integral<T>::value) {
151 return clampToInt32(static_cast<long double>(value));
152 }
153
154 const long double scale =
155 static_cast<long double>(getScaleFactor(decimalPlaces));
156 long double scaled = static_cast<long double>(value) * scale;
157 if (scaled >= 0) {
158 scaled += 0.5;
159 } else {
160 scaled -= 0.5;
161 }
162 return clampToInt32(scaled);
163}
164
165template <typename T>
166T CustomParameterTemplate<T>::decodeValue(int32_t value) const {
167 if (std::is_integral<T>::value) {
168 return static_cast<T>(value);
169 }
170
171 return static_cast<T>(static_cast<long double>(value) /
172 getScaleFactor(decimalPlaces));
173}
174
175template <typename T>
176void CustomParameterTemplate<T>::setStoredValue(int32_t newValue,
177 bool saveToConfig) {
178 if (newValue < minStoredValue) {
179 newValue = minStoredValue;
180 }
181 if (newValue > maxStoredValue) {
182 newValue = maxStoredValue;
183 }
184
185 parameterValue = newValue;
186
187 auto cfg = Supla::Storage::ConfigInstance();
188 if (saveToConfig && cfg) {
189 cfg->setInt32(tag, parameterValue);
190 cfg->saveWithDelay(1000);
191 }
192}
193
194template <typename T>
195void CustomParameterTemplate<T>::send(Supla::WebSender* sender) {
196 auto cfg = Supla::Storage::ConfigInstance();
197 if (cfg) {
198 int32_t storedValue = parameterValue;
199 if (cfg->getInt32(tag, &storedValue)) {
200 setStoredValue(storedValue, false);
201 }
202 }
203
204 sender->formField([&]() {
205 sender->labelFor(tag, label);
206
207 sender->voidTag("input")
208 .attr("type", "number")
209 .attr("step", 1, decimalPlaces)
210 .attr("min", minStoredValue, decimalPlaces)
211 .attr("max", maxStoredValue, decimalPlaces)
212 .attr("name", tag)
213 .attr("id", tag)
214 .attr("value", parameterValue, decimalPlaces)
215 .finish();
216 });
217}
218
219template <typename T>
220bool CustomParameterTemplate<T>::handleResponse(const char* key,
221 const char* value) {
222 if (key == nullptr || value == nullptr || tag == nullptr ||
223 strcmp(key, tag) != 0) {
224 return false;
225 }
226
227 int32_t parsedValue = 0;
228 if (std::is_integral<T>::value) {
229 parsedValue = stringToInt(value);
230 } else {
231 parsedValue = floatStringToInt(value, decimalPlaces);
232 }
233
234 setStoredValue(parsedValue, true);
235 return true;
236}
237
238template <typename T>
239T CustomParameterTemplate<T>::getParameterValue() {
240 auto cfg = Supla::Storage::ConfigInstance();
241 if (cfg) {
242 int32_t storedValue = parameterValue;
243 if (cfg->getInt32(tag, &storedValue)) {
244 setStoredValue(storedValue, false);
245 }
246 }
247 return decodeValue(parameterValue);
248}
249
250template <typename T>
251void CustomParameterTemplate<T>::setParameterValue(T newValue) {
252 setStoredValue(encodeValue(newValue), true);
253}
254
255using CustomParameter = CustomParameterTemplate<int32_t>;
256
257}; // namespace Html
258}; // namespace Supla
259
260#endif // ARDUINO_ARCH_AVR
261#endif // SRC_SUPLA_NETWORK_HTML_CUSTOM_PARAMETER_H_
Definition html_element.h:22
HtmlTag & attr(const char *name, const char *value)
Append a quoted HTML attribute with escaped value.
Definition web_sender.cpp:50
HtmlTag & finish()
Close the opening tag and mark it as finished.
Definition web_sender.cpp:99
This HTML Element provides numeric input in config mode.
Definition custom_parameter.h:32
Definition web_sender.h:160
void formField(Fn &&fn, const char *className="form-field")
Emit a <div class="form-field">...</div> block.
Definition web_sender.h:204
HtmlTag voidTag(const char *tagName)
Start a builder for a void HTML tag such as <input>.
Definition web_sender.h:368
void labelFor(const char *id, const char *text)
Emit a <label for="...">...</label> pair.
Definition web_sender.cpp:138