mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7eb1f061b7 | ||
|
|
5e4f5916f2 | ||
|
|
1450737d94 | ||
|
|
bfd20e559e | ||
|
|
98a7932dee | ||
|
|
19e26d0d64 | ||
|
|
1715218864 | ||
|
|
e503c6cd79 | ||
|
|
9515e3d00b | ||
|
|
53e25ae213 | ||
|
|
4863ecc329 | ||
|
|
b5892f5b5e | ||
|
|
d16502c872 | ||
|
|
e29fb9ba8a | ||
|
|
2ee0411582 | ||
|
|
f210466cb1 | ||
|
|
6af28b1c29 | ||
|
|
049be2484e | ||
|
|
8f438e8045 | ||
|
|
a8382dd6ce | ||
|
|
c55385d6d8 | ||
|
|
87e6691433 | ||
|
|
aa9ba65f70 | ||
|
|
39fef48915 | ||
|
|
362fead7e8 | ||
|
|
e809ed3743 | ||
|
|
dc8c322b42 | ||
|
|
c5688ab632 | ||
|
|
d5d75eee63 | ||
|
|
6ec16733c3 | ||
|
|
ce2b2658ad | ||
|
|
3a866b1aea | ||
|
|
1cf938e16a | ||
|
|
1df427366f | ||
|
|
f97cdcb4d6 | ||
|
|
15c682cd1e | ||
|
|
008983be26 | ||
|
|
2f01000665 | ||
|
|
616955daef | ||
|
|
e22b191a48 | ||
|
|
8930c52ada | ||
|
|
6d94335079 | ||
|
|
f2e0b193af | ||
|
|
5b55902cd9 | ||
|
|
694f647a2c | ||
|
|
fc1cb00523 | ||
|
|
2fda59d7db | ||
|
|
a95837404a | ||
|
|
c6db2a1adf | ||
|
|
f6d22732a0 | ||
|
|
220a69938f | ||
|
|
d438866864 | ||
|
|
2e0ed9ce9f | ||
|
|
5de3b69e2c | ||
|
|
dfd6798377 | ||
|
|
074ae2a5a1 | ||
|
|
77f6a18075 | ||
|
|
0762d9e124 | ||
|
|
37dae04715 | ||
|
|
f299a7ad14 | ||
|
|
ba295385ab | ||
|
|
33adf518ae | ||
|
|
93885d0dd5 | ||
|
|
747cda79db | ||
|
|
f3cfc38adc | ||
|
|
dd3a0a706d | ||
|
|
37d001e7b5 | ||
|
|
add09e5a1c | ||
|
|
561d1c0e55 | ||
|
|
239ba335b1 | ||
|
|
ec83123090 | ||
|
|
4f6d5164a4 | ||
|
|
ae1e2eccd2 | ||
|
|
d7bc821bbe | ||
|
|
f8579f7c96 | ||
|
|
046a9ef6f2 | ||
|
|
64e15542a2 | ||
|
|
7dec674452 | ||
|
|
0f48d3e72c | ||
|
|
1f793c49ae | ||
|
|
e581539cf9 | ||
|
|
736eee79df | ||
|
|
7a0fe3819b | ||
|
|
65c9bf7e52 | ||
|
|
1e61b5670e | ||
|
|
6c2bae6296 | ||
|
|
358d6010b0 | ||
|
|
82978a25c5 | ||
|
|
3519696bae | ||
|
|
bd33df2cc7 | ||
|
|
5f44eb14ad | ||
|
|
fb94cf953a | ||
|
|
d7486218bc | ||
|
|
59913cdc4b | ||
|
|
2d7449aeba | ||
|
|
3ea53a8012 | ||
|
|
48cd12ec3d | ||
|
|
7c71ed2dc6 | ||
|
|
24d8ccc52f | ||
|
|
05cd96f2be | ||
|
|
75795ab1e9 | ||
|
|
bb1602f179 | ||
|
|
ac268f0f73 | ||
|
|
d924567e5f | ||
|
|
109d8df782 | ||
|
|
0aaa35098d | ||
|
|
6f57beab28 | ||
|
|
5b26e27834 | ||
|
|
8429f650aa | ||
|
|
3eb2202117 | ||
|
|
c217a40710 | ||
|
|
4a7308c5bb | ||
|
|
5fba51103e | ||
|
|
95a9808f35 | ||
|
|
c09e180c48 | ||
|
|
b09b650c1d | ||
|
|
44a41b963d | ||
|
|
0510189f54 | ||
|
|
16b3cf764d | ||
|
|
c634c39874 | ||
|
|
ae0846e877 | ||
|
|
40e7e1b418 | ||
|
|
e2a5853dde | ||
|
|
e419e67cb0 | ||
|
|
3356a4ce14 | ||
|
|
26a4347155 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -27,4 +27,4 @@ emsesp
|
||||
/interface/build
|
||||
node_modules
|
||||
/interface/.eslintcache
|
||||
|
||||
test.sh
|
||||
36
CHANGELOG.md
36
CHANGELOG.md
@@ -5,6 +5,42 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
# [3.2.0] August 6 2021
|
||||
|
||||
## Added
|
||||
|
||||
- support for IPv6 (web/api/mqtt, not syslog yet) [#83](https://github.com/emsesp/EMS-ESP32/issues/83)
|
||||
- System Log in Web UI will show current time if the NTP Service is enabled [#82](https://github.com/emsesp/EMS-ESP32/issues/82)
|
||||
- Network settings for Tx-power, WiFi-bandwidth, WiFi-sleepmode [#83](https://github.com/emsesp/EMS-ESP32/issues/83)
|
||||
- optional low CPU clockrate (160 MHz) [#83](https://github.com/emsesp/EMS-ESP32/issues/83)
|
||||
- select format for enumerated values in web
|
||||
- settings for water hysteresis on/off
|
||||
- dallas sensor name editable. `sensorname` console-command, replace sensorid with a unique name [#84](https://github.com/emsesp/EMS-ESP32/issues/84)
|
||||
- 'restart' system command. Can be invoked via API with authentication. [#87](https://github.com/emsesp/EMS-ESP32/issues/87)
|
||||
- add Download button in Web UI for log
|
||||
|
||||
## Fixed
|
||||
|
||||
- set mode allow numbers
|
||||
- Junkers thermostat shows mode as selected by set_mode
|
||||
- HA thermostat mode if bool-format: numbers is selected
|
||||
- Web UI System Log sometimes skipped a few log messages when watching real-time
|
||||
- fix wwactivated [#89](https://github.com/emsesp/EMS-ESP32/issues/89)
|
||||
- don't show commands (like reset) as Device values in the Web or Console
|
||||
|
||||
## Changed
|
||||
|
||||
- removed Rx echo failures counting as incomplete telegrams. Bad telegrams show as Warning and not Errors. [#80](https://github.com/emsesp/EMS-ESP32/issues/80)
|
||||
- add upload_sec to `api/system/info` and removed # from some names to keep consistent with MQTT heartbeat
|
||||
- added debug target to PlatformIO build to help hunt down system crashes
|
||||
- enumerated values always start at zero
|
||||
- maintenance settings for time/date as extra setting
|
||||
- move api/mqtt formats to `settings`, add `enum format`
|
||||
- UI improvements for editing Dallas Sensor details
|
||||
- RESTful GET commands can also require authentication (via bearer access token) for better security
|
||||
- Updated AsyncMqttClient to 0.9.0 and ArduinoJson to 6.18.3
|
||||
- Download buttons for settings and info under the Help tab
|
||||
|
||||
# [3.1.1] June 26 2021
|
||||
|
||||
## Changed
|
||||
|
||||
@@ -5,5 +5,3 @@
|
||||
## Fixed
|
||||
|
||||
## Changed
|
||||
|
||||
## Removed
|
||||
|
||||
@@ -32,8 +32,8 @@ This document describes rules that are in effect for this repository, meant for
|
||||
6. Issues with feature requests should be discussed for viability/desirability.
|
||||
7. Feature requests or changes that are meant to address a very specific/limited use case, especially if at the expense of increased code complexity, may be denied, or may be required to be redesigned, generalized, or simplified.
|
||||
8. Feature requests that are not accompanied by a PR:
|
||||
* could be closed immediately (denied).
|
||||
* could be closed after some predetermined period of time (left as candidate for somebody to pick up).
|
||||
- could be closed immediately (denied).
|
||||
- could be closed after some predetermined period of time (left as candidate for somebody to pick up).
|
||||
9. In some cases, feedback may be requested from the issue reporter, either as additional info for clarification, additional testing, or other. If no feedback is provided, the issue may be closed by a contributor or after 40 days by the STALE bot.
|
||||
|
||||
## Pull requests
|
||||
@@ -49,17 +49,17 @@ The process is straight-forward.
|
||||
- Create a Pull Request against the [**dev**](https://github.com/emsesp/EMS-ESP32/tree/dev) branch of EMS-ESP.
|
||||
|
||||
1. All pull requests must be done against the dev branch.
|
||||
2. Make sure code is formatting per the `.clang-format`
|
||||
3. Only relevant files should be touched (Also beware if your editor has auto-formatting feature enabled).
|
||||
4. Only one feature/fix should be added per PR.
|
||||
5. PRs that don't compile (fail in CI Tests) or cause coding errors will not be merged. Please fix the issue. Same goes for PRs that are raised against older commit in dev - you might need to rebase and resolve conflicts.
|
||||
6. All pull requests should undergo peer review by at least one contributor other than the creator, excepts for the owner.
|
||||
7. All pull requests should consider updates to the documentation.
|
||||
8. Pull requests that address an outstanding issue, particularly an issue deemed to be severe, should be given priority.
|
||||
9. If a PR is accepted, then it should undergo review and updated based on the feedback provided, then merged.
|
||||
10. By submitting a PR, it is needed to use the provided PR template and check all boxes, performing the required tasks and accepting the CLA.
|
||||
11. Pull requests that don't meet the above will be denied and closed.
|
||||
|
||||
2. Make sure code is formatting per the `.clang-format`.
|
||||
3. Make sure any new code is clearly commented explaining what the function/logic does.
|
||||
4. Only relevant files should be touched (Also beware if your editor has auto-formatting feature enabled).
|
||||
5. Only one feature/fix should be added per PR.
|
||||
6. PRs that don't compile (fail in CI Tests) or cause coding errors will not be merged. Please fix the issue. Same goes for PRs that are raised against older commit in dev - you might need to rebase and resolve conflicts.
|
||||
7. All pull requests should undergo peer review by at least one contributor other than the creator, excepts for the owner.
|
||||
8. All pull requests should consider updates to the documentation.
|
||||
9. Pull requests that address an outstanding issue, particularly an issue deemed to be severe, should be given priority.
|
||||
10. If a PR is accepted, then it should undergo review and updated based on the feedback provided, then merged.
|
||||
11. By submitting a PR, it is needed to use the provided PR template and check all boxes, performing the required tasks and accepting the CLA.
|
||||
12. Pull requests that don't meet the above will be denied and closed.
|
||||
|
||||
## Semantic Commit Messages
|
||||
|
||||
@@ -92,7 +92,7 @@ More Examples:
|
||||
|
||||
References:
|
||||
|
||||
- https://www.conventionalcommits.org/
|
||||
- <https://www.conventionalcommits.org/>
|
||||
|
||||
--------------------------------------
|
||||
|
||||
|
||||
@@ -72,9 +72,9 @@ CPPFLAGS += -g3
|
||||
CPPFLAGS += -Os
|
||||
|
||||
CFLAGS += $(CPPFLAGS)
|
||||
# CFLAGS += -Wall
|
||||
# CFLAGS += -Wno-unused -Wno-restrict
|
||||
# CFLAGS += -Wextra
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Wno-unused -Wno-restrict
|
||||
CFLAGS += -Wextra
|
||||
|
||||
CXXFLAGS += $(CFLAGS) -MMD
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
otadata, data, ota, 0xE000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1F0000,
|
||||
app1, app, ota_1, 0x200000, 0x1F0000,
|
||||
spiffs, data, spiffs, 0x3F0000, 0x10000,
|
||||
|
||||
|
5
esp32_partition_debug.csv
Normal file
5
esp32_partition_debug.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xE000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x210000,
|
||||
spiffs, data, spiffs, 0x220000, 0x10000,
|
||||
|
@@ -4,6 +4,7 @@ export const PROJECT_PATH = process.env.REACT_APP_PROJECT_PATH!;
|
||||
export const ENDPOINT_ROOT = calculateEndpointRoot('/rest/');
|
||||
export const WEB_SOCKET_ROOT = calculateWebSocketRoot('/ws/');
|
||||
export const EVENT_SOURCE_ROOT = calculateEndpointRoot('/es/');
|
||||
export const API_ENDPOINT_ROOT = calculateEndpointRoot('/api/');
|
||||
|
||||
function calculateEndpointRoot(endpointPath: string) {
|
||||
const httpRoot = process.env.REACT_APP_HTTP_ROOT;
|
||||
|
||||
@@ -109,7 +109,7 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
||||
/>
|
||||
<TextField
|
||||
name="client_id"
|
||||
label="Client ID (optional)"
|
||||
label="Client ID"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.client_id}
|
||||
@@ -159,7 +159,7 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
||||
value="clean_session"
|
||||
/>
|
||||
}
|
||||
label="Clean Session"
|
||||
label="Set Clean Session"
|
||||
/>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
@@ -169,7 +169,7 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
||||
value="mqtt_retain"
|
||||
/>
|
||||
}
|
||||
label="Retain Flag"
|
||||
label="Use Retain Flag"
|
||||
/>
|
||||
<br></br>
|
||||
<Typography variant="h6" color="primary">
|
||||
@@ -184,34 +184,8 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
||||
onChange={handleValueChange('nested_format')}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={1}>nested on a single topic</MenuItem>
|
||||
<MenuItem value={2}>as individual topics</MenuItem>
|
||||
</SelectValidator>
|
||||
<SelectValidator
|
||||
name="dallas_format"
|
||||
label="Dallas Sensor Payload Grouping"
|
||||
value={data.dallas_format}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('dallas_format')}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={1}>by Sensor ID</MenuItem>
|
||||
<MenuItem value={2}>by Number</MenuItem>
|
||||
</SelectValidator>
|
||||
<SelectValidator
|
||||
name="bool_format"
|
||||
label="Boolean Format"
|
||||
value={data.bool_format}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('bool_format')}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={1}>"on"/"off"</MenuItem>
|
||||
<MenuItem value={2}>true/false</MenuItem>
|
||||
<MenuItem value={3}>1/0</MenuItem>
|
||||
<MenuItem value={4}>"ON"/"OFF"</MenuItem>
|
||||
<MenuItem value={1}>Nested on a single topic</MenuItem>
|
||||
<MenuItem value={2}>As individual topics</MenuItem>
|
||||
</SelectValidator>
|
||||
<SelectValidator
|
||||
name="subscribe_format"
|
||||
@@ -222,9 +196,9 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
||||
onChange={handleValueChange('subscribe_format')}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={0}>general device topic</MenuItem>
|
||||
<MenuItem value={1}>individual topics, main heating circuit</MenuItem>
|
||||
<MenuItem value={2}>individual topics, all heating circuits</MenuItem>
|
||||
<MenuItem value={0}>General device topic</MenuItem>
|
||||
<MenuItem value={1}>Individual topics, main heating circuit</MenuItem>
|
||||
<MenuItem value={2}>Individual topics, all heating circuits</MenuItem>
|
||||
</SelectValidator>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
@@ -246,9 +220,9 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
||||
onChange={handleValueChange('ha_climate_format')}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={1}>use Current temperature (default)</MenuItem>
|
||||
<MenuItem value={2}>use Setpoint temperature</MenuItem>
|
||||
<MenuItem value={3}>Fix to 0</MenuItem>
|
||||
<MenuItem value={1}>Use Current temperature</MenuItem>
|
||||
<MenuItem value={2}>Use Setpoint temperature</MenuItem>
|
||||
<MenuItem value={3}>Always set to 0</MenuItem>
|
||||
</SelectValidator>
|
||||
)}
|
||||
<br></br>
|
||||
|
||||
@@ -34,8 +34,6 @@ export interface MqttSettings {
|
||||
publish_time_mixer: number;
|
||||
publish_time_other: number;
|
||||
publish_time_sensor: number;
|
||||
dallas_format: number;
|
||||
bool_format: number;
|
||||
mqtt_qos: number;
|
||||
mqtt_retain: boolean;
|
||||
ha_enabled: boolean;
|
||||
|
||||
@@ -51,7 +51,11 @@ class NetworkSettingsForm extends React.Component<NetworkStatusFormProps> {
|
||||
ssid: selectedNetwork.ssid,
|
||||
password: '',
|
||||
hostname: props.data.hostname,
|
||||
static_ip_config: false
|
||||
static_ip_config: false,
|
||||
enableIPv6: false,
|
||||
bandwidth20: false,
|
||||
tx_power: 20,
|
||||
nosleep: false
|
||||
};
|
||||
props.setData(networkSettings);
|
||||
}
|
||||
@@ -145,6 +149,53 @@ class NetworkSettingsForm extends React.Component<NetworkStatusFormProps> {
|
||||
onChange={handleValueChange('hostname')}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextValidator
|
||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:20']}
|
||||
errorMessages={[
|
||||
'Tx Power is required',
|
||||
'Must be a number',
|
||||
'Must be greater than 0dBm ',
|
||||
'Max value is 20dBm'
|
||||
]}
|
||||
name="tx_power"
|
||||
label="WiFi Tx Power (dBm)"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.tx_power}
|
||||
type="number"
|
||||
onChange={handleValueChange('tx_power')}
|
||||
margin="normal"
|
||||
/>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
value="enableIPv6"
|
||||
checked={data.enableIPv6}
|
||||
onChange={handleValueChange('enableIPv6')}
|
||||
/>
|
||||
}
|
||||
label="Enable IPv6 support"
|
||||
/>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
value="bandwidth20"
|
||||
checked={data.bandwidth20}
|
||||
onChange={handleValueChange('bandwidth20')}
|
||||
/>
|
||||
}
|
||||
label="Use Lower WiFi Bandwidth"
|
||||
/>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
value="nosleep"
|
||||
checked={data.nosleep}
|
||||
onChange={handleValueChange('nosleep')}
|
||||
/>
|
||||
}
|
||||
label="Disable WiFi Sleep Mode"
|
||||
/>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
@@ -153,7 +204,7 @@ class NetworkSettingsForm extends React.Component<NetworkStatusFormProps> {
|
||||
onChange={handleValueChange('static_ip_config')}
|
||||
/>
|
||||
}
|
||||
label="Static IP Config"
|
||||
label="Use Static IPs"
|
||||
/>
|
||||
{data.static_ip_config && (
|
||||
<Fragment>
|
||||
|
||||
@@ -40,7 +40,22 @@ class NetworkStatusForm extends Component<NetworkStatusFormProps> {
|
||||
if (!status.dns_ip_1) {
|
||||
return 'none';
|
||||
}
|
||||
return status.dns_ip_1 + (status.dns_ip_2 ? ',' + status.dns_ip_2 : '');
|
||||
if (!status.dns_ip_2 || status.dns_ip_2 === '0.0.0.0') {
|
||||
return status.dns_ip_1;
|
||||
}
|
||||
return status.dns_ip_1 + ', ' + status.dns_ip_2;
|
||||
}
|
||||
IPs(status: NetworkStatus) {
|
||||
if (
|
||||
!status.local_ipv6 ||
|
||||
status.local_ipv6 === '0000:0000:0000:0000:0000:0000:0000:0000'
|
||||
) {
|
||||
return status.local_ip;
|
||||
}
|
||||
if (!status.local_ip || status.local_ip === '0.0.0.0') {
|
||||
return status.local_ipv6;
|
||||
}
|
||||
return status.local_ip + ', ' + status.local_ipv6;
|
||||
}
|
||||
|
||||
createListItems() {
|
||||
@@ -77,7 +92,7 @@ class NetworkStatusForm extends Component<NetworkStatusFormProps> {
|
||||
<ListItemAvatar>
|
||||
<Avatar>IP</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="IP Address" secondary={data.local_ip} />
|
||||
<ListItemText primary="IP Address" secondary={this.IPs(data)} />
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
<ListItem>
|
||||
|
||||
@@ -21,6 +21,7 @@ export enum WiFiEncryptionType {
|
||||
export interface NetworkStatus {
|
||||
status: NetworkConnectionStatus;
|
||||
local_ip: string;
|
||||
local_ipv6: string;
|
||||
mac_address: string;
|
||||
rssi: number;
|
||||
ssid: string;
|
||||
@@ -37,6 +38,10 @@ export interface NetworkSettings {
|
||||
password: string;
|
||||
hostname: string;
|
||||
static_ip_config: boolean;
|
||||
enableIPv6: boolean;
|
||||
bandwidth20: boolean;
|
||||
nosleep: boolean;
|
||||
tx_power: number;
|
||||
local_ip?: string;
|
||||
gateway_ip?: string;
|
||||
subnet_mask?: string;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { MenuAppBar } from '../components';
|
||||
import { AuthenticatedRoute } from '../authentication';
|
||||
|
||||
import EMSESPStatusController from './EMSESPStatusController';
|
||||
import EMSESPDevicesController from './EMSESPDevicesController';
|
||||
import EMSESPDataController from './EMSESPDataController';
|
||||
import EMSESPHelp from './EMSESPHelp';
|
||||
|
||||
class EMSESP extends Component<RouteComponentProps> {
|
||||
@@ -24,18 +24,15 @@ class EMSESP extends Component<RouteComponentProps> {
|
||||
onChange={(e, path) => this.handleTabChange(path)}
|
||||
variant="fullWidth"
|
||||
>
|
||||
<Tab
|
||||
value={`/${PROJECT_PATH}/devices`}
|
||||
label="Devices & Sensors"
|
||||
/>
|
||||
<Tab value={`/${PROJECT_PATH}/data`} label="Devices & Sensors" />
|
||||
<Tab value={`/${PROJECT_PATH}/status`} label="EMS Status" />
|
||||
<Tab value={`/${PROJECT_PATH}/help`} label="EMS-ESP Help" />
|
||||
</Tabs>
|
||||
<Switch>
|
||||
<AuthenticatedRoute
|
||||
exact
|
||||
path={`/${PROJECT_PATH}/devices`}
|
||||
component={EMSESPDevicesController}
|
||||
path={`/${PROJECT_PATH}/data`}
|
||||
component={EMSESPDataController}
|
||||
/>
|
||||
<AuthenticatedRoute
|
||||
exact
|
||||
@@ -47,7 +44,7 @@ class EMSESP extends Component<RouteComponentProps> {
|
||||
path={`/${PROJECT_PATH}/help`}
|
||||
component={EMSESPHelp}
|
||||
/>
|
||||
<Redirect to={`/${PROJECT_PATH}/devices`} />
|
||||
<Redirect to={`/${PROJECT_PATH}/data`} />
|
||||
</Switch>
|
||||
</MenuAppBar>
|
||||
);
|
||||
|
||||
35
interface/src/project/EMSESPDataController.tsx
Normal file
35
interface/src/project/EMSESPDataController.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import {
|
||||
restController,
|
||||
RestControllerProps,
|
||||
RestFormLoader,
|
||||
SectionContent
|
||||
} from '../components';
|
||||
|
||||
import { ENDPOINT_ROOT } from '../api';
|
||||
import EMSESPDataForm from './EMSESPDataForm';
|
||||
import { EMSESPData } from './EMSESPtypes';
|
||||
|
||||
export const EMSESP_DATA_ENDPOINT = ENDPOINT_ROOT + 'data';
|
||||
|
||||
type EMSESPDataControllerProps = RestControllerProps<EMSESPData>;
|
||||
|
||||
class EMSESPDataController extends Component<EMSESPDataControllerProps> {
|
||||
componentDidMount() {
|
||||
this.props.loadData();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SectionContent title="Devices & Sensors">
|
||||
<RestFormLoader
|
||||
{...this.props}
|
||||
render={(formProps) => <EMSESPDataForm {...formProps} />}
|
||||
/>
|
||||
</SectionContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default restController(EMSESP_DATA_ENDPOINT, EMSESPDataController);
|
||||
@@ -40,21 +40,24 @@ import {
|
||||
import { RestFormProps, FormButton, extractEventValue } from '../components';
|
||||
|
||||
import {
|
||||
EMSESPDevices,
|
||||
EMSESPData,
|
||||
EMSESPDeviceData,
|
||||
Device,
|
||||
DeviceValue,
|
||||
DeviceValueUOM,
|
||||
DeviceValueUOM_s
|
||||
DeviceValueUOM_s,
|
||||
Sensor
|
||||
} from './EMSESPtypes';
|
||||
|
||||
import ValueForm from './ValueForm';
|
||||
import SensorForm from './SensorForm';
|
||||
|
||||
import { ENDPOINT_ROOT } from '../api';
|
||||
|
||||
export const SCANDEVICES_ENDPOINT = ENDPOINT_ROOT + 'scanDevices';
|
||||
export const DEVICE_DATA_ENDPOINT = ENDPOINT_ROOT + 'deviceData';
|
||||
export const WRITE_VALUE_ENDPOINT = ENDPOINT_ROOT + 'writeValue';
|
||||
export const WRITE_SENSOR_ENDPOINT = ENDPOINT_ROOT + 'writeSensor';
|
||||
|
||||
const StyledTableCell = withStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -88,15 +91,16 @@ function compareDevices(a: Device, b: Device) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
interface EMSESPDevicesFormState {
|
||||
interface EMSESPDataFormState {
|
||||
confirmScanDevices: boolean;
|
||||
processing: boolean;
|
||||
deviceData?: EMSESPDeviceData;
|
||||
selectedDevice?: number;
|
||||
edit_devicevalue?: DeviceValue;
|
||||
edit_Sensor?: Sensor;
|
||||
}
|
||||
|
||||
type EMSESPDevicesFormProps = RestFormProps<EMSESPDevices> &
|
||||
type EMSESPDataFormProps = RestFormProps<EMSESPData> &
|
||||
AuthenticatedContextProps &
|
||||
WithWidthProps;
|
||||
|
||||
@@ -118,18 +122,27 @@ export const formatDuration = (duration_min: number) => {
|
||||
const pluralize = (count: number, noun: string, suffix = 's') =>
|
||||
` ${count} ${noun}${count !== 1 ? suffix : ''} `;
|
||||
|
||||
function formatValue(value: any, uom: number) {
|
||||
function formatValue(value: any, uom: number, digit: number) {
|
||||
switch (uom) {
|
||||
case DeviceValueUOM.HOURS:
|
||||
return value ? formatDuration(value * 60) : '0 hours';
|
||||
case DeviceValueUOM.MINUTES:
|
||||
return value ? formatDuration(value) : '0 minutes';
|
||||
case DeviceValueUOM.NONE:
|
||||
case DeviceValueUOM.LIST:
|
||||
return value;
|
||||
case DeviceValueUOM.NUM:
|
||||
return new Intl.NumberFormat().format(value);
|
||||
case DeviceValueUOM.BOOLEAN:
|
||||
return value ? 'on' : 'off';
|
||||
case DeviceValueUOM.DEGREES:
|
||||
return (
|
||||
new Intl.NumberFormat(undefined, {
|
||||
minimumFractionDigits: digit
|
||||
}).format(value) +
|
||||
' ' +
|
||||
DeviceValueUOM_s[uom]
|
||||
);
|
||||
default:
|
||||
return (
|
||||
new Intl.NumberFormat().format(value) + ' ' + DeviceValueUOM_s[uom]
|
||||
@@ -137,16 +150,16 @@ function formatValue(value: any, uom: number) {
|
||||
}
|
||||
}
|
||||
|
||||
class EMSESPDevicesForm extends Component<
|
||||
EMSESPDevicesFormProps,
|
||||
EMSESPDevicesFormState
|
||||
class EMSESPDataForm extends Component<
|
||||
EMSESPDataFormProps,
|
||||
EMSESPDataFormState
|
||||
> {
|
||||
state: EMSESPDevicesFormState = {
|
||||
state: EMSESPDataFormState = {
|
||||
confirmScanDevices: false,
|
||||
processing: false
|
||||
};
|
||||
|
||||
handleValueChange = (name: keyof DeviceValue) => (
|
||||
handleDeviceValueChange = (name: keyof DeviceValue) => (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
this.setState({
|
||||
@@ -157,11 +170,11 @@ class EMSESPDevicesForm extends Component<
|
||||
});
|
||||
};
|
||||
|
||||
cancelEditingValue = () => {
|
||||
cancelEditingDeviceValue = () => {
|
||||
this.setState({ edit_devicevalue: undefined });
|
||||
};
|
||||
|
||||
doneEditingValue = () => {
|
||||
doneEditingDeviceValue = () => {
|
||||
const { edit_devicevalue, selectedDevice } = this.state;
|
||||
|
||||
redirectingAuthorizedFetch(WRITE_VALUE_ENDPOINT, {
|
||||
@@ -179,6 +192,7 @@ class EMSESPDevicesForm extends Component<
|
||||
this.props.enqueueSnackbar('Write command sent to device', {
|
||||
variant: 'success'
|
||||
});
|
||||
this.handleRowClick(selectedDevice);
|
||||
} else if (response.status === 204) {
|
||||
this.props.enqueueSnackbar('Write command failed', {
|
||||
variant: 'error'
|
||||
@@ -206,6 +220,72 @@ class EMSESPDevicesForm extends Component<
|
||||
this.setState({ edit_devicevalue: dv });
|
||||
};
|
||||
|
||||
handleSensorChange = (name: keyof Sensor) => (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
this.setState({
|
||||
edit_Sensor: {
|
||||
...this.state.edit_Sensor!,
|
||||
[name]: extractEventValue(event)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
cancelEditingSensor = () => {
|
||||
this.setState({ edit_Sensor: undefined });
|
||||
};
|
||||
|
||||
doneEditingSensor = () => {
|
||||
const { edit_Sensor } = this.state;
|
||||
|
||||
redirectingAuthorizedFetch(WRITE_SENSOR_ENDPOINT, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
// because input field with type=number doens't like negative values, force it here
|
||||
sensor: {
|
||||
no: edit_Sensor?.no,
|
||||
id: edit_Sensor?.id,
|
||||
temp: edit_Sensor?.temp,
|
||||
offset: Number(edit_Sensor?.offset)
|
||||
}
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
this.props.enqueueSnackbar('Sensor updated', {
|
||||
variant: 'success'
|
||||
});
|
||||
this.props.loadData();
|
||||
} else if (response.status === 204) {
|
||||
this.props.enqueueSnackbar('Sensor change failed', {
|
||||
variant: 'error'
|
||||
});
|
||||
} else if (response.status === 403) {
|
||||
this.props.enqueueSnackbar('Write access denied', {
|
||||
variant: 'error'
|
||||
});
|
||||
} else {
|
||||
throw Error('Unexpected response code: ' + response.status);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.props.enqueueSnackbar(error.message || 'Problem writing value', {
|
||||
variant: 'error'
|
||||
});
|
||||
});
|
||||
|
||||
if (edit_Sensor) {
|
||||
this.setState({ edit_Sensor: undefined });
|
||||
}
|
||||
};
|
||||
|
||||
sendSensor = (sn: Sensor) => {
|
||||
this.setState({ edit_Sensor: sn });
|
||||
};
|
||||
|
||||
noDevices = () => {
|
||||
return this.props.data.devices.length === 0;
|
||||
};
|
||||
@@ -236,7 +316,7 @@ class EMSESPDevicesForm extends Component<
|
||||
<TableRow
|
||||
hover
|
||||
key={device.id}
|
||||
onClick={() => this.handleRowClick(device)}
|
||||
onClick={() => this.handleRowClick(device.id)}
|
||||
>
|
||||
<TableCell>
|
||||
<CustomTooltip
|
||||
@@ -289,30 +369,49 @@ class EMSESPDevicesForm extends Component<
|
||||
|
||||
renderSensorItems() {
|
||||
const { data } = this.props;
|
||||
const me = this.props.authenticatedContext.me;
|
||||
return (
|
||||
<TableContainer>
|
||||
<p></p>
|
||||
<Typography variant="h6" color="primary" paragraph>
|
||||
Dallas Sensors
|
||||
Sensors
|
||||
</Typography>
|
||||
{!this.noSensors() && (
|
||||
<Table size="small" padding="default">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<StyledTableCell
|
||||
padding="checkbox"
|
||||
style={{ width: 18 }}
|
||||
></StyledTableCell>
|
||||
<StyledTableCell>Sensor #</StyledTableCell>
|
||||
<StyledTableCell align="center">ID</StyledTableCell>
|
||||
<StyledTableCell align="left">ID / Name</StyledTableCell>
|
||||
<StyledTableCell align="right">Temperature</StyledTableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{data.sensors.map((sensorData) => (
|
||||
<TableRow key={sensorData.no}>
|
||||
<TableRow key={sensorData.no} hover>
|
||||
<TableCell padding="checkbox" style={{ width: 18 }}>
|
||||
{me.admin && (
|
||||
<CustomTooltip title="edit" placement="left-end">
|
||||
<IconButton
|
||||
edge="start"
|
||||
size="small"
|
||||
aria-label="Edit"
|
||||
onClick={() => this.sendSensor(sensorData)}
|
||||
>
|
||||
<EditIcon color="primary" fontSize="small" />
|
||||
</IconButton>
|
||||
</CustomTooltip>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell component="th" scope="row">
|
||||
{sensorData.no}
|
||||
</TableCell>
|
||||
<TableCell align="center">{sensorData.id}</TableCell>
|
||||
<TableCell align="left">{sensorData.id}</TableCell>
|
||||
<TableCell align="right">
|
||||
{formatValue(sensorData.temp, DeviceValueUOM.DEGREES)}
|
||||
{formatValue(sensorData.temp, DeviceValueUOM.DEGREES, 1)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
@@ -322,7 +421,7 @@ class EMSESPDevicesForm extends Component<
|
||||
{this.noSensors() && (
|
||||
<Box color="warning.main" p={0} mt={0} mb={0}>
|
||||
<Typography variant="body1">
|
||||
<i>no external temperature sensors were detected</i>
|
||||
<i>no connected Dallas temperature sensors were detected</i>
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
@@ -330,6 +429,34 @@ class EMSESPDevicesForm extends Component<
|
||||
);
|
||||
}
|
||||
|
||||
renderAnalog() {
|
||||
const { data } = this.props;
|
||||
return (
|
||||
<TableContainer>
|
||||
{data.analog > 0 && (
|
||||
<Table size="small" padding="default">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<StyledTableCell>Sensortype</StyledTableCell>
|
||||
<StyledTableCell align="right">Value</StyledTableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
Analog Input
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{formatValue(data.analog, DeviceValueUOM.MV, 0)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</TableContainer>
|
||||
);
|
||||
}
|
||||
|
||||
renderScanDevicesDialog() {
|
||||
return (
|
||||
<Dialog
|
||||
@@ -396,10 +523,10 @@ class EMSESPDevicesForm extends Component<
|
||||
};
|
||||
|
||||
handleRowClick = (device: any) => {
|
||||
this.setState({ selectedDevice: device.id, deviceData: undefined });
|
||||
this.setState({ selectedDevice: device, deviceData: undefined });
|
||||
redirectingAuthorizedFetch(DEVICE_DATA_ENDPOINT, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ id: device.id }),
|
||||
body: JSON.stringify({ id: device }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
@@ -475,7 +602,7 @@ class EMSESPDevicesForm extends Component<
|
||||
{item.n}
|
||||
</TableCell>
|
||||
<TableCell padding="none" align="right">
|
||||
{formatValue(item.v, item.u)}
|
||||
{formatValue(item.v, item.u, 0)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
@@ -495,13 +622,14 @@ class EMSESPDevicesForm extends Component<
|
||||
}
|
||||
|
||||
render() {
|
||||
const { edit_devicevalue } = this.state;
|
||||
const { edit_devicevalue, edit_Sensor } = this.state;
|
||||
return (
|
||||
<Fragment>
|
||||
<br></br>
|
||||
{this.renderDeviceItems()}
|
||||
{this.renderDeviceData()}
|
||||
{this.renderSensorItems()}
|
||||
{this.renderAnalog()}
|
||||
<br></br>
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
<Box flexGrow={1} padding={1}>
|
||||
@@ -528,9 +656,17 @@ class EMSESPDevicesForm extends Component<
|
||||
{edit_devicevalue && (
|
||||
<ValueForm
|
||||
devicevalue={edit_devicevalue}
|
||||
onDoneEditing={this.doneEditingValue}
|
||||
onCancelEditing={this.cancelEditingValue}
|
||||
handleValueChange={this.handleValueChange}
|
||||
onDoneEditing={this.doneEditingDeviceValue}
|
||||
onCancelEditing={this.cancelEditingDeviceValue}
|
||||
handleValueChange={this.handleDeviceValueChange}
|
||||
/>
|
||||
)}
|
||||
{edit_Sensor && (
|
||||
<SensorForm
|
||||
sensor={edit_Sensor}
|
||||
onDoneEditing={this.doneEditingSensor}
|
||||
onCancelEditing={this.cancelEditingSensor}
|
||||
handleSensorChange={this.handleSensorChange}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
@@ -538,4 +674,4 @@ class EMSESPDevicesForm extends Component<
|
||||
}
|
||||
}
|
||||
|
||||
export default withAuthenticatedContext(withWidth()(EMSESPDevicesForm));
|
||||
export default withAuthenticatedContext(withWidth()(EMSESPDataForm));
|
||||
@@ -1,35 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import {
|
||||
restController,
|
||||
RestControllerProps,
|
||||
RestFormLoader,
|
||||
SectionContent
|
||||
} from '../components';
|
||||
|
||||
import { ENDPOINT_ROOT } from '../api';
|
||||
import EMSESPDevicesForm from './EMSESPDevicesForm';
|
||||
import { EMSESPDevices } from './EMSESPtypes';
|
||||
|
||||
export const EMSESP_DEVICES_ENDPOINT = ENDPOINT_ROOT + 'allDevices';
|
||||
|
||||
type EMSESPDevicesControllerProps = RestControllerProps<EMSESPDevices>;
|
||||
|
||||
class EMSESPDevicesController extends Component<EMSESPDevicesControllerProps> {
|
||||
componentDidMount() {
|
||||
this.props.loadData();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SectionContent title="Devices & Sensors">
|
||||
<RestFormLoader
|
||||
{...this.props}
|
||||
render={(formProps) => <EMSESPDevicesForm {...formProps} />}
|
||||
/>
|
||||
</SectionContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default restController(EMSESP_DEVICES_ENDPOINT, EMSESPDevicesController);
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Component } from 'react';
|
||||
import {
|
||||
Typography,
|
||||
Box,
|
||||
@@ -14,14 +14,40 @@ import CommentIcon from '@material-ui/icons/CommentTwoTone';
|
||||
import MenuBookIcon from '@material-ui/icons/MenuBookTwoTone';
|
||||
import GitHubIcon from '@material-ui/icons/GitHub';
|
||||
import StarIcon from '@material-ui/icons/Star';
|
||||
import ImportExportIcon from '@material-ui/icons/ImportExport';
|
||||
import BugReportIcon from '@material-ui/icons/BugReportTwoTone';
|
||||
import DownloadIcon from '@material-ui/icons/GetApp';
|
||||
|
||||
export const WebAPISystemSettings =
|
||||
window.location.origin + '/api/system/settings';
|
||||
export const WebAPISystemInfo = window.location.origin + '/api/system/info';
|
||||
import { FormButton } from '../components';
|
||||
|
||||
import { API_ENDPOINT_ROOT } from '../api';
|
||||
|
||||
import { redirectingAuthorizedFetch } from '../authentication';
|
||||
|
||||
class EMSESPHelp extends Component {
|
||||
onDownload = (endpoint: string) => {
|
||||
redirectingAuthorizedFetch(API_ENDPOINT_ROOT + 'system/' + endpoint)
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.json();
|
||||
}
|
||||
throw Error(
|
||||
'Device returned unexpected response code: ' + response.status
|
||||
);
|
||||
})
|
||||
.then((json) => {
|
||||
const a = document.createElement('a');
|
||||
const filename = 'emsesp_system_' + endpoint + '.txt';
|
||||
a.href = URL.createObjectURL(
|
||||
new Blob([JSON.stringify(json, null, 2)], {
|
||||
type: 'text/plain'
|
||||
})
|
||||
);
|
||||
a.setAttribute('download', filename);
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SectionContent title="EMS-ESP Help" titleGutter>
|
||||
@@ -31,9 +57,9 @@ class EMSESPHelp extends Component {
|
||||
<MenuBookIcon />
|
||||
</ListItemAvatar>
|
||||
<ListItemText>
|
||||
For the latest news and updates go to the{' '}
|
||||
For help and information on the latest updates visit the{' '}
|
||||
<Link href="https://emsesp.github.io/docs" color="primary">
|
||||
{'official documentation'} website
|
||||
{'online documentation'}
|
||||
</Link>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
@@ -55,41 +81,36 @@ class EMSESPHelp extends Component {
|
||||
<GitHubIcon />
|
||||
</ListItemAvatar>
|
||||
<ListItemText>
|
||||
To report an issue or feature request go to{' '}
|
||||
To report an issue or request a feature go to{' '}
|
||||
<Link
|
||||
href="https://github.com/emsesp/EMS-ESP32/issues/new/choose"
|
||||
color="primary"
|
||||
>
|
||||
{'click here'}
|
||||
</Link>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<ImportExportIcon />
|
||||
</ListItemAvatar>
|
||||
<ListItemText>
|
||||
To export your system settings{' '}
|
||||
<Link target="_blank" href={WebAPISystemSettings} color="primary">
|
||||
{'click here'}
|
||||
</Link>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<BugReportIcon />
|
||||
</ListItemAvatar>
|
||||
<ListItemText>
|
||||
To export the current status of EMS-ESP{' '}
|
||||
<Link target="_blank" href={WebAPISystemInfo} color="primary">
|
||||
{'click here'}
|
||||
{'GitHub'}
|
||||
</Link>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
<Box flexWrap="none" padding={1} whiteSpace="nowrap">
|
||||
<FormButton
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => this.onDownload('info')}
|
||||
>
|
||||
download system info
|
||||
</FormButton>
|
||||
<FormButton
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => this.onDownload('settings')}
|
||||
>
|
||||
download all settings
|
||||
</FormButton>
|
||||
</Box>
|
||||
|
||||
<Box bgcolor="info.main" border={1} p={3} mt={1} mb={0}>
|
||||
<Typography variant="h6">
|
||||
EMS-ESP is free and open-source.
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
BlockFormControlLabel
|
||||
} from '../components';
|
||||
|
||||
import { isIP, optional } from '../validators';
|
||||
import { isIPv4, optional, isHostname, or } from '../validators';
|
||||
|
||||
import { EMSESPSettings } from './EMSESPtypes';
|
||||
|
||||
@@ -55,7 +55,10 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
ValidatorForm.addValidationRule('isOptionalIP', optional(isIP));
|
||||
ValidatorForm.addValidationRule(
|
||||
'isOptionalIPorHost',
|
||||
optional(or(isIPv4, isHostname))
|
||||
);
|
||||
}
|
||||
|
||||
changeBoardProfile = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
@@ -109,15 +112,17 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
<ValidatorForm onSubmit={saveData}>
|
||||
<Box bgcolor="info.main" p={2} mt={2} mb={2}>
|
||||
<Typography variant="body1">
|
||||
Adjust any of the EMS-ESP settings here. For help refer to the{' '}
|
||||
<i>
|
||||
Refer to the
|
||||
<Link
|
||||
target="_blank"
|
||||
href="https://emsesp.github.io/docs/#/Configure-firmware32?id=ems-esp-settings"
|
||||
color="primary"
|
||||
>
|
||||
{'online documentation'}
|
||||
{' documentation'}
|
||||
</Link>
|
||||
.
|
||||
for information on each setting
|
||||
</i>
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -202,7 +207,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
<Typography variant="body2">
|
||||
<i>
|
||||
Select a pre-configured board layout to automatically set the GPIO
|
||||
pins, or set your own custom configuration
|
||||
pins. Select "Custom..." to view or manually edit the values.
|
||||
</i>
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -365,7 +370,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
|
||||
<br></br>
|
||||
<Typography variant="h6" color="primary">
|
||||
Options
|
||||
General Options
|
||||
</Typography>
|
||||
|
||||
{data.led_gpio !== 0 && (
|
||||
@@ -390,20 +395,10 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
value="dallas_parasite"
|
||||
/>
|
||||
}
|
||||
label="Enable Dallas parasite mode"
|
||||
label="Use Dallas Sensor parasite power"
|
||||
/>
|
||||
)}
|
||||
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={data.notoken_api}
|
||||
onChange={handleValueChange('notoken_api')}
|
||||
value="notoken_api"
|
||||
/>
|
||||
}
|
||||
label="Bypass Access Token authorization on API calls"
|
||||
/>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
@@ -414,6 +409,26 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
}
|
||||
label="Enable ADC"
|
||||
/>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={data.low_clock}
|
||||
onChange={handleValueChange('low_clock')}
|
||||
value="low_clock"
|
||||
/>
|
||||
}
|
||||
label="Run at a lower CPU clock speed"
|
||||
/>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={data.notoken_api}
|
||||
onChange={handleValueChange('notoken_api')}
|
||||
value="notoken_api"
|
||||
/>
|
||||
}
|
||||
label="Bypass Access Token authorization on API calls"
|
||||
/>
|
||||
<Grid
|
||||
container
|
||||
spacing={0}
|
||||
@@ -443,6 +458,65 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<br></br>
|
||||
<Typography variant="h6" color="primary">
|
||||
Formatting Options
|
||||
</Typography>
|
||||
|
||||
<Grid
|
||||
container
|
||||
spacing={1}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Grid item xs={4}>
|
||||
<SelectValidator
|
||||
name="bool_format"
|
||||
label="Boolean Format"
|
||||
value={data.bool_format}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('bool_format')}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={1}>"on"/"off"</MenuItem>
|
||||
<MenuItem value={2}>"ON"/"OFF"</MenuItem>
|
||||
<MenuItem value={3}>true/false</MenuItem>
|
||||
<MenuItem value={4}>1/0</MenuItem>
|
||||
</SelectValidator>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<SelectValidator
|
||||
name="enum_format"
|
||||
label="Enum Format"
|
||||
value={data.enum_format}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('enum_format')}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={1}>Text</MenuItem>
|
||||
<MenuItem value={2}>Number</MenuItem>
|
||||
</SelectValidator>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<SelectValidator
|
||||
name="dallas_format"
|
||||
label="Dallas Sensor Format"
|
||||
value={data.dallas_format}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('dallas_format')}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={1}>ID</MenuItem>
|
||||
<MenuItem value={2}>Number</MenuItem>
|
||||
<MenuItem value={3}>Name</MenuItem>
|
||||
</SelectValidator>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<br></br>
|
||||
<Typography variant="h6" color="primary">
|
||||
Syslog
|
||||
@@ -469,10 +543,10 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
>
|
||||
<Grid item xs={5}>
|
||||
<TextValidator
|
||||
validators={['isOptionalIP']}
|
||||
errorMessages={['Not a valid IP address']}
|
||||
validators={['isOptionalIPorHost']}
|
||||
errorMessages={['Not a valid IPv4 address or hostname']}
|
||||
name="syslog_host"
|
||||
label="IP"
|
||||
label="Host"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.syslog_host}
|
||||
@@ -537,7 +611,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
'Max value is 10'
|
||||
]}
|
||||
name="syslog_mark_interval"
|
||||
label="Mark Interval seconds (0=off)"
|
||||
label="Mark Interval (seconds, 0=off)"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.syslog_mark_interval}
|
||||
@@ -554,7 +628,7 @@ class EMSESPSettingsForm extends Component<EMSESPSettingsFormProps> {
|
||||
value="trace_raw"
|
||||
/>
|
||||
}
|
||||
label="Output EMS telegrams in raw format"
|
||||
label="Output EMS telegrams as hexadecimal bytes"
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
@@ -16,11 +16,15 @@ export interface EMSESPSettings {
|
||||
dallas_parasite: boolean;
|
||||
led_gpio: number;
|
||||
hide_led: boolean;
|
||||
low_clock: boolean;
|
||||
notoken_api: boolean;
|
||||
analog_enabled: boolean;
|
||||
pbutton_gpio: number;
|
||||
trace_raw: boolean;
|
||||
board_profile: string;
|
||||
bool_format: number;
|
||||
dallas_format: number;
|
||||
enum_format: number;
|
||||
}
|
||||
|
||||
export enum busConnectionStatus {
|
||||
@@ -50,12 +54,14 @@ export interface Device {
|
||||
export interface Sensor {
|
||||
no: number;
|
||||
id: string;
|
||||
temp: string;
|
||||
temp: number;
|
||||
offset: number;
|
||||
}
|
||||
|
||||
export interface EMSESPDevices {
|
||||
export interface EMSESPData {
|
||||
devices: Device[];
|
||||
sensors: Sensor[];
|
||||
analog: number;
|
||||
}
|
||||
|
||||
export interface DeviceValue {
|
||||
@@ -63,6 +69,7 @@ export interface DeviceValue {
|
||||
u: number;
|
||||
n: string;
|
||||
c: string;
|
||||
l: string[];
|
||||
}
|
||||
|
||||
export interface EMSESPDeviceData {
|
||||
@@ -87,7 +94,9 @@ export enum DeviceValueUOM {
|
||||
SECONDS,
|
||||
DBM,
|
||||
NUM,
|
||||
BOOLEAN
|
||||
BOOLEAN,
|
||||
LIST,
|
||||
MV
|
||||
}
|
||||
|
||||
export const DeviceValueUOM_s = [
|
||||
@@ -107,5 +116,7 @@ export const DeviceValueUOM_s = [
|
||||
'seconds',
|
||||
'dBm',
|
||||
'number',
|
||||
'on/off'
|
||||
'on/off',
|
||||
'',
|
||||
'mV'
|
||||
];
|
||||
|
||||
@@ -23,7 +23,7 @@ class ProjectMenu extends Component<ProjectProps> {
|
||||
to="/ems-esp/"
|
||||
selected={
|
||||
path.startsWith('/ems-esp/status') ||
|
||||
path.startsWith('/ems-esp/devices') ||
|
||||
path.startsWith('/ems-esp/data') ||
|
||||
path.startsWith('/ems-esp/help')
|
||||
}
|
||||
button
|
||||
|
||||
101
interface/src/project/SensorForm.tsx
Normal file
101
interface/src/project/SensorForm.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import React, { RefObject } from 'react';
|
||||
import { ValidatorForm, TextValidator } from 'react-material-ui-form-validator';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions
|
||||
} from '@material-ui/core';
|
||||
|
||||
import { FormButton } from '../components';
|
||||
import { Sensor } from './EMSESPtypes';
|
||||
|
||||
interface SensorFormProps {
|
||||
sensor: Sensor;
|
||||
onDoneEditing: () => void;
|
||||
onCancelEditing: () => void;
|
||||
handleSensorChange: (
|
||||
data: keyof Sensor
|
||||
) => (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
class SensorForm extends React.Component<SensorFormProps> {
|
||||
formRef: RefObject<any> = React.createRef();
|
||||
|
||||
submit = () => {
|
||||
this.formRef.current.submit();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
sensor,
|
||||
handleSensorChange,
|
||||
onDoneEditing,
|
||||
onCancelEditing
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ValidatorForm onSubmit={onDoneEditing} ref={this.formRef}>
|
||||
<Dialog
|
||||
maxWidth="xs"
|
||||
onClose={onCancelEditing}
|
||||
aria-labelledby="user-form-dialog-title"
|
||||
open
|
||||
>
|
||||
<DialogTitle id="user-form-dialog-title">
|
||||
Editing Sensor #{sensor.no}
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<TextValidator
|
||||
validators={['matchRegexp:^([a-zA-Z0-9_.-]{0,19})$']}
|
||||
errorMessages={['Not a valid name']}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={sensor.id}
|
||||
onChange={handleSensorChange('id')}
|
||||
margin="normal"
|
||||
label="Name"
|
||||
name="id"
|
||||
/>
|
||||
<TextValidator
|
||||
validators={['isFloat', 'minFloat:-5', 'maxFloat:5']}
|
||||
errorMessages={[
|
||||
'Must be a number',
|
||||
'Must be greater than -5',
|
||||
'Max value is +5'
|
||||
]}
|
||||
label="Custom Offset (°C)"
|
||||
name="offset"
|
||||
type="number"
|
||||
value={sensor.offset}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
InputProps={{ inputProps: { min: '-5', max: '5', step: '0.1' } }}
|
||||
margin="normal"
|
||||
onChange={handleSensorChange('offset')}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<FormButton
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={onCancelEditing}
|
||||
>
|
||||
Cancel
|
||||
</FormButton>
|
||||
<FormButton
|
||||
variant="contained"
|
||||
color="primary"
|
||||
type="submit"
|
||||
onClick={this.submit}
|
||||
>
|
||||
Done
|
||||
</FormButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</ValidatorForm>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SensorForm;
|
||||
@@ -51,9 +51,25 @@ class ValueForm extends React.Component<ValueFormProps> {
|
||||
>
|
||||
<DialogTitle id="user-form-dialog-title">Change Value</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
{devicevalue.u !== DeviceValueUOM.BOOLEAN && (
|
||||
{devicevalue.u === DeviceValueUOM.LIST && (
|
||||
<TextField
|
||||
id="outlined-select-value"
|
||||
select
|
||||
value={devicevalue.v}
|
||||
autoFocus
|
||||
fullWidth
|
||||
onChange={handleValueChange('v')}
|
||||
variant="outlined"
|
||||
>
|
||||
{devicevalue.l.map((val) => (
|
||||
<MenuItem value={val}>{val}</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
)}
|
||||
{devicevalue.u !== DeviceValueUOM.BOOLEAN &&
|
||||
devicevalue.u !== DeviceValueUOM.LIST && (
|
||||
<OutlinedInput
|
||||
id="outlined-adornment-value"
|
||||
id="value"
|
||||
value={devicevalue.v}
|
||||
autoFocus
|
||||
fullWidth
|
||||
@@ -63,15 +79,11 @@ class ValueForm extends React.Component<ValueFormProps> {
|
||||
{DeviceValueUOM_s[devicevalue.u]}
|
||||
</InputAdornment>
|
||||
}
|
||||
aria-describedby="outlined-value-helper-text"
|
||||
inputProps={{
|
||||
'aria-label': 'value'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{devicevalue.u === DeviceValueUOM.BOOLEAN && (
|
||||
<TextField
|
||||
id="outlined-select-value"
|
||||
id="selected-value"
|
||||
select
|
||||
value={devicevalue.v}
|
||||
autoFocus
|
||||
@@ -83,9 +95,7 @@ class ValueForm extends React.Component<ValueFormProps> {
|
||||
<MenuItem value="false">off</MenuItem>
|
||||
</TextField>
|
||||
)}
|
||||
<FormHelperText id="outlined-value-helper-text">
|
||||
{devicevalue.n}
|
||||
</FormHelperText>
|
||||
<FormHelperText>{devicevalue.n}</FormHelperText>
|
||||
<Box color="warning.main" p={0} pl={0} pr={0} mt={4} mb={0}>
|
||||
<Typography variant="body2">
|
||||
<i>
|
||||
|
||||
@@ -44,7 +44,6 @@ class GenerateToken extends React.Component<
|
||||
}
|
||||
})
|
||||
.then((generatedToken) => {
|
||||
// console.log(generatedToken);
|
||||
this.setState({ token: generatedToken.token });
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -6,6 +6,8 @@ import { useWindowSize } from '../components';
|
||||
|
||||
interface LogEventConsoleProps {
|
||||
events: LogEvent[];
|
||||
compact: boolean;
|
||||
level: number;
|
||||
}
|
||||
|
||||
interface Offsets {
|
||||
@@ -63,7 +65,9 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||
const LogEventConsole: FC<LogEventConsoleProps> = (props) => {
|
||||
useWindowSize();
|
||||
const classes = useStyles({ topOffset, leftOffset });
|
||||
const { events } = props;
|
||||
const { events, compact, level } = props;
|
||||
|
||||
const filter_events = events.filter((e) => e.l <= level);
|
||||
|
||||
const styleLevel = (level: LogLevel) => {
|
||||
switch (level) {
|
||||
@@ -103,23 +107,34 @@ const LogEventConsole: FC<LogEventConsoleProps> = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const paddedLevelLabel = (level: LogLevel) => {
|
||||
const paddedLevelLabel = (level: LogLevel, compact: boolean) => {
|
||||
const label = levelLabel(level);
|
||||
return label.padStart(8, '\xa0');
|
||||
return compact ? ' ' + label[0] : label.padStart(8, '\xa0');
|
||||
};
|
||||
|
||||
const paddedNameLabel = (name: string) => {
|
||||
const paddedNameLabel = (name: string, compact: boolean) => {
|
||||
const label = '[' + name + ']';
|
||||
return label.padStart(8, '\xa0');
|
||||
return compact ? label : label.padEnd(12, '\xa0');
|
||||
};
|
||||
|
||||
const paddedIDLabel = (id: number, compact: boolean) => {
|
||||
const label = id + ':';
|
||||
return compact ? label : label.padEnd(7, '\xa0');
|
||||
};
|
||||
|
||||
return (
|
||||
<Box id="log-window" className={classes.console}>
|
||||
{events.map((e) => (
|
||||
<div className={classes.entry}>
|
||||
{filter_events.map((e) => (
|
||||
<div className={classes.entry} key={e.i}>
|
||||
<span>{e.t}</span>
|
||||
<span className={styleLevel(e.l)}>{paddedLevelLabel(e.l)} </span>
|
||||
<span>{paddedNameLabel(e.n)} </span>
|
||||
{compact && <span>{paddedLevelLabel(e.l, compact)} </span>}
|
||||
{!compact && (
|
||||
<span className={styleLevel(e.l)}>
|
||||
{paddedLevelLabel(e.l, compact)}{' '}
|
||||
</span>
|
||||
)}
|
||||
<span>{paddedIDLabel(e.i, compact)} </span>
|
||||
<span>{paddedNameLabel(e.n, compact)} </span>
|
||||
<span>{e.m}</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -3,22 +3,38 @@ import { Component } from 'react';
|
||||
import {
|
||||
restController,
|
||||
RestControllerProps,
|
||||
RestFormLoader,
|
||||
SectionContent
|
||||
SectionContent,
|
||||
BlockFormControlLabel
|
||||
} from '../components';
|
||||
|
||||
import { addAccessTokenParameter } from '../authentication';
|
||||
import {
|
||||
ValidatorForm,
|
||||
SelectValidator
|
||||
} from 'react-material-ui-form-validator';
|
||||
|
||||
import {
|
||||
Grid,
|
||||
Slider,
|
||||
FormLabel,
|
||||
Checkbox,
|
||||
MenuItem,
|
||||
Button
|
||||
} from '@material-ui/core';
|
||||
|
||||
import {
|
||||
addAccessTokenParameter,
|
||||
redirectingAuthorizedFetch
|
||||
} from '../authentication';
|
||||
|
||||
import DownloadIcon from '@material-ui/icons/GetApp';
|
||||
|
||||
import { ENDPOINT_ROOT, EVENT_SOURCE_ROOT } from '../api';
|
||||
export const FETCH_LOG_ENDPOINT = ENDPOINT_ROOT + 'fetchLog';
|
||||
export const LOG_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'logSettings';
|
||||
|
||||
export const LOG_EVENT_EVENT_SOURCE_URL = EVENT_SOURCE_ROOT + 'log';
|
||||
|
||||
import LogEventForm from './LogEventForm';
|
||||
import LogEventConsole from './LogEventConsole';
|
||||
|
||||
import { LogEvent, LogSettings } from './types';
|
||||
import { LogEvent, LogSettings, LogLevel } from './types';
|
||||
|
||||
import { Decoder } from '@msgpack/msgpack';
|
||||
const decoder = new Decoder();
|
||||
@@ -26,6 +42,9 @@ const decoder = new Decoder();
|
||||
interface LogEventControllerState {
|
||||
eventSource?: EventSource;
|
||||
events: LogEvent[];
|
||||
compact: boolean;
|
||||
level: number;
|
||||
max_messages: number;
|
||||
}
|
||||
|
||||
type LogEventControllerProps = RestControllerProps<LogSettings>;
|
||||
@@ -40,12 +59,15 @@ class LogEventController extends Component<
|
||||
constructor(props: LogEventControllerProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
events: []
|
||||
events: [],
|
||||
compact: false,
|
||||
level: 6,
|
||||
max_messages: 25
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.loadData();
|
||||
this.fetchValues();
|
||||
this.fetchLog();
|
||||
this.configureEventSource();
|
||||
}
|
||||
@@ -59,6 +81,15 @@ class LogEventController extends Component<
|
||||
}
|
||||
}
|
||||
|
||||
changeCompact = (
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
checked: boolean
|
||||
) => {
|
||||
this.setState({
|
||||
compact: checked
|
||||
});
|
||||
};
|
||||
|
||||
fetchLog = () => {
|
||||
fetch(FETCH_LOG_ENDPOINT)
|
||||
.then((response) => {
|
||||
@@ -78,6 +109,25 @@ class LogEventController extends Component<
|
||||
});
|
||||
};
|
||||
|
||||
fetchValues = () => {
|
||||
redirectingAuthorizedFetch(LOG_SETTINGS_ENDPOINT)
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.json();
|
||||
}
|
||||
throw Error('Unexpected status code: ' + response.status);
|
||||
})
|
||||
.then((json) => {
|
||||
this.setState({ level: json.level, max_messages: json.max_messages });
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorMessage = error.message || 'Unknown error';
|
||||
this.props.enqueueSnackbar('Problem fetching: ' + errorMessage, {
|
||||
variant: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
configureEventSource = () => {
|
||||
this.eventSource = new EventSource(
|
||||
addAccessTokenParameter(LOG_EVENT_EVENT_SOURCE_URL)
|
||||
@@ -102,14 +152,172 @@ class LogEventController extends Component<
|
||||
}
|
||||
};
|
||||
|
||||
changeMaxMessages = (
|
||||
event: React.ChangeEvent<{}>,
|
||||
value: number | number[]
|
||||
) => {
|
||||
this.setState({
|
||||
max_messages: value as number
|
||||
});
|
||||
this.send_data(this.state.level, value as number);
|
||||
};
|
||||
|
||||
changeLevel = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
this.setState({
|
||||
level: parseInt(event.target.value)
|
||||
});
|
||||
this.send_data(parseInt(event.target.value), this.state.max_messages);
|
||||
};
|
||||
|
||||
send_data = (level: number, max_messages: number) => {
|
||||
redirectingAuthorizedFetch(LOG_SETTINGS_ENDPOINT, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
level: level,
|
||||
max_messages: max_messages
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
throw Error('Unexpected response code: ' + response.status);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.props.enqueueSnackbar(
|
||||
error.message || 'Problem applying log settings',
|
||||
{ variant: 'warning' }
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
levelLabel = (level: LogLevel) => {
|
||||
switch (level) {
|
||||
case LogLevel.ERROR:
|
||||
return 'E';
|
||||
case LogLevel.WARNING:
|
||||
return 'W';
|
||||
case LogLevel.NOTICE:
|
||||
return 'N';
|
||||
case LogLevel.INFO:
|
||||
return 'I';
|
||||
case LogLevel.DEBUG:
|
||||
return 'D';
|
||||
case LogLevel.TRACE:
|
||||
return 'TRACE';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
onDownload = () => {
|
||||
const { events, level } = this.state;
|
||||
let result = '';
|
||||
for (const i in events) {
|
||||
if (events[i].l <= level) {
|
||||
result +=
|
||||
events[i].t +
|
||||
' ' +
|
||||
this.levelLabel(events[i].l) +
|
||||
' ' +
|
||||
events[i].i +
|
||||
': [' +
|
||||
events[i].n +
|
||||
'] ' +
|
||||
events[i].m +
|
||||
'\n';
|
||||
}
|
||||
}
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute(
|
||||
'href',
|
||||
'data:text/plain;charset=utf-8,' + encodeURIComponent(result)
|
||||
);
|
||||
a.setAttribute('download', 'log.txt');
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { saveData } = this.props;
|
||||
return (
|
||||
<SectionContent title="System Log" id="log-window">
|
||||
<RestFormLoader
|
||||
{...this.props}
|
||||
render={(formProps) => <LogEventForm {...formProps} />}
|
||||
<ValidatorForm onSubmit={saveData}>
|
||||
<Grid
|
||||
container
|
||||
spacing={3}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid item xs={2}>
|
||||
<SelectValidator
|
||||
name="level"
|
||||
label="Log Level"
|
||||
value={this.state.level}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={this.changeLevel}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={3}>ERROR</MenuItem>
|
||||
<MenuItem value={4}>WARNING</MenuItem>
|
||||
<MenuItem value={5}>NOTICE</MenuItem>
|
||||
<MenuItem value={6}>INFO</MenuItem>
|
||||
<MenuItem value={7}>DEBUG</MenuItem>
|
||||
<MenuItem value={8}>ALL</MenuItem>
|
||||
</SelectValidator>
|
||||
</Grid>
|
||||
<Grid item xs={2}>
|
||||
<FormLabel>Buffer size</FormLabel>
|
||||
<Slider
|
||||
value={this.state.max_messages}
|
||||
valueLabelDisplay="auto"
|
||||
name="max_messages"
|
||||
marks={[
|
||||
{ value: 25, label: '25' },
|
||||
{ value: 50, label: '50' },
|
||||
{ value: 75, label: '75' }
|
||||
]}
|
||||
step={25}
|
||||
min={25}
|
||||
max={75}
|
||||
onChange={this.changeMaxMessages}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={this.state.compact}
|
||||
onChange={this.changeCompact}
|
||||
value="compact"
|
||||
/>
|
||||
}
|
||||
label="Compact Layout"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item md>
|
||||
<Button
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={this.onDownload}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ValidatorForm>
|
||||
|
||||
<LogEventConsole
|
||||
level={this.state.level}
|
||||
compact={this.state.compact}
|
||||
events={this.state.events}
|
||||
/>
|
||||
<LogEventConsole events={this.state.events} />
|
||||
</SectionContent>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
import { Component } from 'react';
|
||||
|
||||
import {
|
||||
ValidatorForm,
|
||||
SelectValidator
|
||||
} from 'react-material-ui-form-validator';
|
||||
|
||||
import { Typography, Grid } from '@material-ui/core';
|
||||
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
|
||||
import {
|
||||
redirectingAuthorizedFetch,
|
||||
withAuthenticatedContext,
|
||||
AuthenticatedContextProps
|
||||
} from '../authentication';
|
||||
|
||||
import { RestFormProps } from '../components';
|
||||
import { LogSettings } from './types';
|
||||
|
||||
import { ENDPOINT_ROOT } from '../api';
|
||||
export const LOG_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'logSettings';
|
||||
|
||||
type LogEventFormProps = AuthenticatedContextProps & RestFormProps<LogSettings>;
|
||||
|
||||
class LogEventForm extends Component<LogEventFormProps> {
|
||||
changeLevel = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const { data, setData } = this.props;
|
||||
setData({
|
||||
...data,
|
||||
level: parseInt(event.target.value)
|
||||
});
|
||||
|
||||
redirectingAuthorizedFetch(LOG_SETTINGS_ENDPOINT, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ level: event.target.value }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.json();
|
||||
}
|
||||
throw Error('Unexpected response code: ' + response.status);
|
||||
})
|
||||
.then((json) => {
|
||||
this.props.enqueueSnackbar('Log settings changed', {
|
||||
variant: 'success'
|
||||
});
|
||||
setData({
|
||||
...data,
|
||||
level: json.level
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.props.enqueueSnackbar(
|
||||
error.message || 'Problem changing log settings',
|
||||
{ variant: 'warning' }
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { data, saveData } = this.props;
|
||||
return (
|
||||
<ValidatorForm onSubmit={saveData}>
|
||||
<Grid
|
||||
container
|
||||
spacing={0}
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid item xs={2}>
|
||||
<SelectValidator
|
||||
name="level"
|
||||
label="Log Level"
|
||||
value={data.level}
|
||||
variant="outlined"
|
||||
onChange={this.changeLevel}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value={-1}>OFF</MenuItem>
|
||||
<MenuItem value={3}>ERROR</MenuItem>
|
||||
<MenuItem value={4}>WARNING</MenuItem>
|
||||
<MenuItem value={5}>NOTICE</MenuItem>
|
||||
<MenuItem value={6}>INFO</MenuItem>
|
||||
<MenuItem value={7}>DEBUG</MenuItem>
|
||||
<MenuItem value={8}>ALL</MenuItem>
|
||||
</SelectValidator>
|
||||
</Grid>
|
||||
<Grid item md>
|
||||
<Typography color="primary" variant="body2">
|
||||
<i>
|
||||
(the last {data.max_messages} messages are buffered and new log
|
||||
events are shown in real time)
|
||||
</i>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ValidatorForm>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withAuthenticatedContext(LogEventForm);
|
||||
@@ -50,6 +50,7 @@ export enum LogLevel {
|
||||
export interface LogEvent {
|
||||
t: string;
|
||||
l: LogLevel;
|
||||
i: number;
|
||||
n: string;
|
||||
m: string;
|
||||
}
|
||||
|
||||
@@ -3,3 +3,4 @@ export { default as isIP } from './isIP';
|
||||
export { default as optional } from './optional';
|
||||
export { default as or } from './or';
|
||||
export { default as isPath } from './isPath';
|
||||
export { default as isIPv4 } from './isIPv4';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const ipAddressRegexp = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||
const ipAddressRegexp = /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/;
|
||||
|
||||
export default function isIp(ipAddress: string) {
|
||||
return ipAddressRegexp.test(ipAddress);
|
||||
|
||||
5
interface/src/validators/isIPv4.ts
Normal file
5
interface/src/validators/isIPv4.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
const ipv4AddressRegexp = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||
|
||||
export default function isIpv4(ipAddress: string) {
|
||||
return ipv4AddressRegexp.test(ipAddress);
|
||||
}
|
||||
@@ -1,6 +1,31 @@
|
||||
ArduinoJson: change log
|
||||
=======================
|
||||
|
||||
v6.18.3 (2021-07-27)
|
||||
-------
|
||||
|
||||
* Changed return type of `convertToJson()` and `Converter<T>::toJson()` to `void`
|
||||
* Added `as<std::string_view>()` and `is<std::string_view>()`
|
||||
|
||||
v6.18.2 (2021-07-19)
|
||||
-------
|
||||
|
||||
* Removed a symlink because the Arduino Library Specification forbids it
|
||||
|
||||
v6.18.1 (2021-07-03)
|
||||
-------
|
||||
|
||||
* Fixed support for `volatile float` and `volatile double` (issue #1557)
|
||||
* Fixed error `[Pe070]: incomplete type is not allowed` on IAR (issue #1560)
|
||||
* Fixed `serializeJson(doc, String)` when allocation fails (issue #1572)
|
||||
* Fixed clang-tidy warnings (issue #1574, PR #1577 by @armandas)
|
||||
* Added fake class `InvalidConversion<T1,T2>` to easily identify invalid conversions (issue #1585)
|
||||
* Added support for `std::string_view` (issue #1578, PR #1554 by @0xFEEDC0DE64)
|
||||
* Fixed warning `definition of implicit copy constructor for 'MsgPackDeserializer' is deprecated because it has a user-declared copy assignment operator`
|
||||
* Added `JsonArray::clear()` (issue #1597)
|
||||
* Fixed `JsonVariant::as<unsigned>()` (issue #1601)
|
||||
* Added support for ESP-IDF component build (PR #1562 by @qt1, PR #1599 by @andreaskuster)
|
||||
|
||||
v6.18.0 (2021-05-05)
|
||||
-------
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
---
|
||||
|
||||
[](https://www.ardu-badge.com/ArduinoJson/6.18.0)
|
||||
[](https://www.ardu-badge.com/ArduinoJson/6.18.3)
|
||||
[](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A6.x)
|
||||
[](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
|
||||
@@ -33,15 +33,15 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
|
||||
* [Optionally works without heap memory (zero malloc)](https://arduinojson.org/v6/api/staticjsondocument/?utm_source=github&utm_medium=readme)
|
||||
* Deduplicates strings
|
||||
* Versatile
|
||||
* [Supports custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/?utm_source=github&utm_medium=readme)
|
||||
* Supports [Arduino's `String`](https://arduinojson.org/v6/api/config/enable_arduino_string/?utm_source=github&utm_medium=readme) and [STL's `std::string`](https://arduinojson.org/v6/api/config/enable_std_string/?utm_source=github&utm_medium=readme)
|
||||
* Supports [Arduino's `Stream`](https://arduinojson.org/v6/api/config/enable_arduino_stream/?utm_source=github&utm_medium=readme) and [STL's `std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/?utm_source=github&utm_medium=readme)
|
||||
* [Supports Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/?utm_source=github&utm_medium=readme)
|
||||
* Supports [custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/?utm_source=github&utm_medium=readme)
|
||||
* Supports [`String`](https://arduinojson.org/v6/api/config/enable_arduino_string/?utm_source=github&utm_medium=readme), [`std::string`](https://arduinojson.org/v6/api/config/enable_std_string/?utm_source=github&utm_medium=readme) and [`std::string_view`](https://arduinojson.org/v6/api/config/enable_string_view/?utm_source=github&utm_medium=readme)
|
||||
* Supports [`Stream`](https://arduinojson.org/v6/api/config/enable_arduino_stream/?utm_source=github&utm_medium=readme) and [`std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/?utm_source=github&utm_medium=readme)
|
||||
* Supports [Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/?utm_source=github&utm_medium=readme)
|
||||
* Supports [custom readers](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme#custom-reader) and [custom writers](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme#custom-writer)
|
||||
* Supports custom converters
|
||||
* Supports [custom converters](https://arduinojson.org/news/2021/05/04/version-6-18-0/?utm_source=github&utm_medium=readme)
|
||||
* Portable
|
||||
* Usable on any C++ project (not limited to Arduino)
|
||||
* Compatible with C++98
|
||||
* Compatible with C++98, C++11, C++14 and C++17
|
||||
* Zero warnings with `-Wall -Wextra -pedantic` and `/W4`
|
||||
* [Header-only library](https://en.wikipedia.org/wiki/Header-only)
|
||||
* Works with virtually any board
|
||||
@@ -81,15 +81,17 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
|
||||
* [GCC 4.4, 4.6, 4.7, 4.8, 4.9, 5, 6, 7, 8, 9, 10](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
|
||||
* [Clang 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 5.0, 6.0, 7, 8, 9, 10](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
|
||||
* [Continuously fuzzed with Google OSS Fuzz](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
|
||||
* Passes all default checks of [clang-tidy](https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/)
|
||||
* Well documented
|
||||
* [Tutorials](https://arduinojson.org/v6/doc/deserialization/?utm_source=github&utm_medium=readme)
|
||||
* [Examples](https://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme)
|
||||
* [How-tos](https://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme)
|
||||
* [FAQ](https://arduinojson.org/v6/faq/?utm_source=github&utm_medium=readme)
|
||||
* [Troubleshooter](https://arduinojson.org/v6/troubleshooter/?utm_source=github&utm_medium=readme)
|
||||
* [Book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme)
|
||||
* [Changelog](CHANGELOG.md)
|
||||
* Vibrant user community
|
||||
* Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories) and [PlatformIO](https://platformio.org/lib/search)
|
||||
* Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories)
|
||||
* [Used in hundreds of projects](https://www.hackster.io/search?i=projects&q=arduinojson)
|
||||
* [Responsive support](https://github.com/bblanchon/ArduinoJson/issues?q=is%3Aissue+is%3Aclosed)
|
||||
|
||||
@@ -132,9 +134,11 @@ serializeJson(doc, Serial);
|
||||
|
||||
See the [tutorial on arduinojson.org](https://arduinojson.org/doc/encoding/?utm_source=github&utm_medium=readme)
|
||||
|
||||
## Support the project
|
||||
## Support the project ❤️
|
||||
|
||||
Do you like this library? Please [star this project on GitHub](https://github.com/bblanchon/ArduinoJson/stargazers)!
|
||||
Do you like this library?
|
||||
Please [star this project on GitHub](https://github.com/bblanchon/ArduinoJson/stargazers)!
|
||||
|
||||
What? You don't like it but you *love* it?
|
||||
We don't take donations anymore, but [we sell a book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme), so you can help and learn at the same time.
|
||||
You can support the project by [purchasing my book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme).
|
||||
Alternatively, you can make a recurring donation via [GitHub Sponsors](https://github.com/sponsors/bblanchon).
|
||||
|
||||
@@ -161,14 +161,20 @@ class ArrayRef : public ArrayRefBase<CollectionData>,
|
||||
_data->removeElement(index);
|
||||
}
|
||||
|
||||
void clear() const {
|
||||
if (!_data)
|
||||
return;
|
||||
_data->clear();
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool* _pool;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<ArrayConstRef> {
|
||||
static bool toJson(VariantConstRef src, VariantRef dst) {
|
||||
return variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
static void toJson(VariantConstRef src, VariantRef dst) {
|
||||
variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
}
|
||||
|
||||
static ArrayConstRef fromJson(VariantConstRef src) {
|
||||
@@ -183,8 +189,8 @@ struct Converter<ArrayConstRef> {
|
||||
|
||||
template <>
|
||||
struct Converter<ArrayRef> {
|
||||
static bool toJson(VariantConstRef src, VariantRef dst) {
|
||||
return variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
static void toJson(VariantConstRef src, VariantRef dst) {
|
||||
variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
}
|
||||
|
||||
static ArrayRef fromJson(VariantRef src) {
|
||||
@@ -193,6 +199,8 @@ struct Converter<ArrayRef> {
|
||||
return ArrayRef(pool, data != 0 ? data->asArray() : 0);
|
||||
}
|
||||
|
||||
static InvalidConversion<VariantConstRef, ArrayRef> fromJson(VariantConstRef);
|
||||
|
||||
static bool checkJson(VariantConstRef) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -178,8 +178,8 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
|
||||
return _array.getOrAddElement(_index);
|
||||
}
|
||||
|
||||
friend bool convertToJson(const this_type& src, VariantRef dst) {
|
||||
return dst.set(src.getUpstreamElement());
|
||||
friend void convertToJson(const this_type& src, VariantRef dst) {
|
||||
dst.set(src.getUpstreamElement());
|
||||
}
|
||||
|
||||
TArray _array;
|
||||
|
||||
@@ -62,9 +62,9 @@ inline bool CollectionData::copyFrom(const CollectionData& src,
|
||||
VariantData* var;
|
||||
if (s->key() != 0) {
|
||||
if (s->ownsKey())
|
||||
var = addMember(RamStringAdapter(s->key()), pool);
|
||||
var = addMember(adaptString(const_cast<char*>(s->key())), pool);
|
||||
else
|
||||
var = addMember(ConstRamStringAdapter(s->key()), pool);
|
||||
var = addMember(adaptString(s->key()), pool);
|
||||
} else {
|
||||
var = addElement(pool);
|
||||
}
|
||||
@@ -107,7 +107,7 @@ template <typename TAdaptedString>
|
||||
inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const {
|
||||
VariantSlot* slot = _head;
|
||||
while (slot) {
|
||||
if (key.equals(slot->key()))
|
||||
if (key.compare(slot->key()) == 0)
|
||||
break;
|
||||
slot = slot->next();
|
||||
}
|
||||
|
||||
@@ -6,14 +6,20 @@
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
# define ARDUINOJSON_HAS_LONG_LONG 1
|
||||
#define ARDUINOJSON_HAS_NULLPTR 1
|
||||
# define ARDUINOJSON_HAS_RVALUE_REFERENCES 1
|
||||
#else
|
||||
# define ARDUINOJSON_HAS_LONG_LONG 0
|
||||
#define ARDUINOJSON_HAS_NULLPTR 0
|
||||
# define ARDUINOJSON_HAS_RVALUE_REFERENCES 0
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINOJSON_HAS_NULLPTR
|
||||
# if __cplusplus >= 201103L
|
||||
# define ARDUINOJSON_HAS_NULLPTR 1
|
||||
# else
|
||||
# define ARDUINOJSON_HAS_NULLPTR 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !ARDUINOJSON_HAS_LONG_LONG
|
||||
# define ARDUINOJSON_HAS_INT64 1
|
||||
#else
|
||||
@@ -56,6 +62,17 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINOJSON_ENABLE_STRING_VIEW
|
||||
# ifdef __has_include
|
||||
# if __has_include(<string_view>) && __cplusplus >= 201703L
|
||||
# define ARDUINOJSON_ENABLE_STRING_VIEW 1
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#ifndef ARDUINOJSON_ENABLE_STRING_VIEW
|
||||
# define ARDUINOJSON_ENABLE_STRING_VIEW 0
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_EMBEDDED_MODE
|
||||
|
||||
// Store floats by default to reduce the memory usage (issue #134)
|
||||
@@ -213,7 +230,8 @@
|
||||
|
||||
#ifndef ARDUINOJSON_LITTLE_ENDIAN
|
||||
# if defined(_MSC_VER) || \
|
||||
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
|
||||
(defined(__BYTE_ORDER__) && \
|
||||
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
|
||||
defined(__LITTLE_ENDIAN__) || defined(__i386) || defined(__x86_64)
|
||||
# define ARDUINOJSON_LITTLE_ENDIAN 1
|
||||
# else
|
||||
|
||||
@@ -32,7 +32,7 @@ class JsonDocument : public Visitable {
|
||||
|
||||
void clear() {
|
||||
_pool.clear();
|
||||
_data.setNull();
|
||||
_data.init();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -304,15 +304,15 @@ class JsonDocument : public Visitable {
|
||||
|
||||
protected:
|
||||
JsonDocument() : _pool(0, 0) {
|
||||
_data.setNull();
|
||||
_data.init();
|
||||
}
|
||||
|
||||
JsonDocument(MemoryPool pool) : _pool(pool) {
|
||||
_data.setNull();
|
||||
_data.init();
|
||||
}
|
||||
|
||||
JsonDocument(char* buf, size_t capa) : _pool(buf, capa) {
|
||||
_data.setNull();
|
||||
_data.init();
|
||||
}
|
||||
|
||||
~JsonDocument() {}
|
||||
@@ -337,8 +337,8 @@ class JsonDocument : public Visitable {
|
||||
JsonDocument& operator=(const JsonDocument&);
|
||||
};
|
||||
|
||||
inline bool convertToJson(const JsonDocument& src, VariantRef dst) {
|
||||
return dst.set(src.as<VariantConstRef>());
|
||||
inline void convertToJson(const JsonDocument& src, VariantRef dst) {
|
||||
dst.set(src.as<VariantConstRef>());
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -45,7 +45,8 @@ class Latch {
|
||||
}
|
||||
|
||||
TReader _reader;
|
||||
char _current;
|
||||
char _current; // NOLINT(clang-analyzer-optin.cplusplus.UninitializedObject)
|
||||
// Not initialized in constructor (+10 bytes on AVR)
|
||||
bool _loaded;
|
||||
#if ARDUINOJSON_DEBUG
|
||||
bool _ended;
|
||||
|
||||
@@ -155,7 +155,6 @@ class TextFormatter {
|
||||
|
||||
protected:
|
||||
CountingDecorator<TWriter> _writer;
|
||||
size_t _length;
|
||||
|
||||
private:
|
||||
TextFormatter &operator=(const TextFormatter &); // cannot be assigned
|
||||
|
||||
@@ -31,7 +31,7 @@ inline bool isLowSurrogate(uint16_t codeunit) {
|
||||
|
||||
class Codepoint {
|
||||
public:
|
||||
Codepoint() : _highSurrogate(0) {}
|
||||
Codepoint() : _highSurrogate(0), _codepoint(0) {}
|
||||
|
||||
bool append(uint16_t codeunit) {
|
||||
if (isHighSurrogate(codeunit)) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <ArduinoJson/Memory/Alignment.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/mpl/max.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <ArduinoJson/Variant/VariantSlot.hpp>
|
||||
|
||||
#include <string.h> // memmove
|
||||
@@ -37,7 +38,8 @@ class MemoryPool {
|
||||
}
|
||||
|
||||
void* buffer() {
|
||||
return _begin;
|
||||
return _begin; // NOLINT(clang-analyzer-unix.Malloc)
|
||||
// movePointers() alters this pointer
|
||||
}
|
||||
|
||||
// Gets the capacity of the memoryPool in bytes
|
||||
@@ -63,7 +65,7 @@ class MemoryPool {
|
||||
return 0;
|
||||
|
||||
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||
const char* existingCopy = findString(str.begin());
|
||||
const char* existingCopy = findString(str);
|
||||
if (existingCopy)
|
||||
return existingCopy;
|
||||
#endif
|
||||
@@ -85,7 +87,7 @@ class MemoryPool {
|
||||
|
||||
const char* saveStringFromFreeZone(size_t len) {
|
||||
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||
const char* dup = findString(_left);
|
||||
const char* dup = findString(adaptString(_left));
|
||||
if (dup)
|
||||
return dup;
|
||||
#endif
|
||||
@@ -162,16 +164,11 @@ class MemoryPool {
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||
template <typename TIterator>
|
||||
const char* findString(TIterator str) {
|
||||
template <typename TAdaptedString>
|
||||
const char* findString(const TAdaptedString& str) {
|
||||
for (char* next = _begin; next < _left; ++next) {
|
||||
char* begin = next;
|
||||
|
||||
// try to match
|
||||
for (TIterator it = str; *it == *next; ++it) {
|
||||
if (*next++ == 0)
|
||||
return begin;
|
||||
}
|
||||
if (str.compare(next) == 0)
|
||||
return next;
|
||||
|
||||
// jump to next terminator
|
||||
while (*next) ++next;
|
||||
|
||||
@@ -16,31 +16,30 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
template <typename TReader, typename TStringStorage>
|
||||
class MsgPackDeserializer {
|
||||
public:
|
||||
MsgPackDeserializer(MemoryPool & pool, TReader reader, TStringStorage stringStorage)
|
||||
: _pool(&pool)
|
||||
, _reader(reader)
|
||||
, _stringStorage(stringStorage)
|
||||
, _error(DeserializationError::Ok)
|
||||
, _foundSomething(false) {
|
||||
}
|
||||
MsgPackDeserializer(MemoryPool &pool, TReader reader,
|
||||
TStringStorage stringStorage)
|
||||
: _pool(&pool),
|
||||
_reader(reader),
|
||||
_stringStorage(stringStorage),
|
||||
_error(DeserializationError::Ok),
|
||||
_foundSomething(false) {}
|
||||
|
||||
template <typename TFilter>
|
||||
DeserializationError parse(VariantData & variant, TFilter filter, NestingLimit nestingLimit) {
|
||||
DeserializationError parse(VariantData &variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
parseVariant(&variant, filter, nestingLimit);
|
||||
return _foundSomething ? _error : DeserializationError::EmptyInput;
|
||||
}
|
||||
|
||||
private:
|
||||
// Prevent VS warning "assignment operator could not be generated"
|
||||
MsgPackDeserializer & operator=(const MsgPackDeserializer &);
|
||||
|
||||
bool invalidInput() {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename TFilter>
|
||||
bool parseVariant(VariantData * variant, TFilter filter, NestingLimit nestingLimit) {
|
||||
bool parseVariant(VariantData *variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
uint8_t code = 0;
|
||||
if (!readByte(code))
|
||||
return false;
|
||||
@@ -270,7 +269,8 @@ class MsgPackDeserializer {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<sizeof(T) == 4, bool>::type readFloat(VariantData * variant) {
|
||||
typename enable_if<sizeof(T) == 4, bool>::type readFloat(
|
||||
VariantData *variant) {
|
||||
T value;
|
||||
if (!readBytes(value))
|
||||
return false;
|
||||
@@ -280,7 +280,8 @@ class MsgPackDeserializer {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<sizeof(T) == 8, bool>::type readDouble(VariantData * variant) {
|
||||
typename enable_if<sizeof(T) == 8, bool>::type readDouble(
|
||||
VariantData *variant) {
|
||||
T value;
|
||||
if (!readBytes(value))
|
||||
return false;
|
||||
@@ -290,7 +291,8 @@ class MsgPackDeserializer {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<sizeof(T) == 4, bool>::type readDouble(VariantData * variant) {
|
||||
typename enable_if<sizeof(T) == 4, bool>::type readDouble(
|
||||
VariantData *variant) {
|
||||
uint8_t i[8]; // input is 8 bytes
|
||||
T value; // output is 4 bytes
|
||||
uint8_t *o = reinterpret_cast<uint8_t *>(&value);
|
||||
@@ -329,7 +331,8 @@ class MsgPackDeserializer {
|
||||
bool readString(VariantData *variant, size_t n) {
|
||||
if (!readString(n))
|
||||
return false;
|
||||
variant->setStringPointer(_stringStorage.save(), typename TStringStorage::storage_policy());
|
||||
variant->setStringPointer(_stringStorage.save(),
|
||||
typename TStringStorage::storage_policy());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -351,7 +354,8 @@ class MsgPackDeserializer {
|
||||
}
|
||||
|
||||
template <typename TSize, typename TFilter>
|
||||
bool readArray(VariantData * variant, TFilter filter, NestingLimit nestingLimit) {
|
||||
bool readArray(VariantData *variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
TSize size;
|
||||
if (!readInteger(size))
|
||||
return false;
|
||||
@@ -359,7 +363,8 @@ class MsgPackDeserializer {
|
||||
}
|
||||
|
||||
template <typename TFilter>
|
||||
bool readArray(VariantData * variant, size_t n, TFilter filter, NestingLimit nestingLimit) {
|
||||
bool readArray(VariantData *variant, size_t n, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
@@ -392,7 +397,8 @@ class MsgPackDeserializer {
|
||||
}
|
||||
|
||||
template <typename TSize, typename TFilter>
|
||||
bool readObject(VariantData * variant, TFilter filter, NestingLimit nestingLimit) {
|
||||
bool readObject(VariantData *variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
TSize size;
|
||||
if (!readInteger(size))
|
||||
return false;
|
||||
@@ -400,7 +406,8 @@ class MsgPackDeserializer {
|
||||
}
|
||||
|
||||
template <typename TFilter>
|
||||
bool readObject(VariantData * variant, size_t n, TFilter filter, NestingLimit nestingLimit) {
|
||||
bool readObject(VariantData *variant, size_t n, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
@@ -484,17 +491,24 @@ class MsgPackDeserializer {
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TString>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, const TString & input, NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, AllowAllFilter());
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, const TString &input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TString>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, const TString & input, Filter filter, NestingLimit nestingLimit = NestingLimit()) {
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, const TString &input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TString>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, const TString & input, NestingLimit nestingLimit, Filter filter) {
|
||||
DeserializationError deserializeMsgPack(JsonDocument &doc, const TString &input,
|
||||
NestingLimit nestingLimit,
|
||||
Filter filter) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
@@ -503,17 +517,24 @@ DeserializationError deserializeMsgPack(JsonDocument & doc, const TString & inpu
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, TStream & input, NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, AllowAllFilter());
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TStream &input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, TStream & input, Filter filter, NestingLimit nestingLimit = NestingLimit()) {
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TStream &input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, TStream & input, NestingLimit nestingLimit, Filter filter) {
|
||||
DeserializationError deserializeMsgPack(JsonDocument &doc, TStream &input,
|
||||
NestingLimit nestingLimit,
|
||||
Filter filter) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
@@ -522,17 +543,24 @@ DeserializationError deserializeMsgPack(JsonDocument & doc, TStream & input, Nes
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, AllowAllFilter());
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TChar *input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, Filter filter, NestingLimit nestingLimit = NestingLimit()) {
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TChar *input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, NestingLimit nestingLimit, Filter filter) {
|
||||
DeserializationError deserializeMsgPack(JsonDocument &doc, TChar *input,
|
||||
NestingLimit nestingLimit,
|
||||
Filter filter) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
@@ -541,18 +569,28 @@ DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, Nesti
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, size_t inputSize, NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit, AllowAllFilter());
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TChar *input, size_t inputSize,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, size_t inputSize, Filter filter, NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit, filter);
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TChar *input, size_t inputSize, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, size_t inputSize, NestingLimit nestingLimit, Filter filter) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit, filter);
|
||||
DeserializationError deserializeMsgPack(JsonDocument &doc, TChar *input,
|
||||
size_t inputSize,
|
||||
NestingLimit nestingLimit,
|
||||
Filter filter) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
filter);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
ARDUINOJSON_VERSION_MINOR, \
|
||||
ARDUINOJSON_VERSION_REVISION), \
|
||||
_, \
|
||||
ARDUINOJSON_HEX_DIGIT(ARDUINOJSON_ENABLE_PROGMEM, \
|
||||
ARDUINOJSON_USE_LONG_LONG, ARDUINOJSON_USE_DOUBLE, \
|
||||
ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \
|
||||
ARDUINOJSON_HEX_DIGIT( \
|
||||
ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_USE_LONG_LONG, \
|
||||
ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \
|
||||
ARDUINOJSON_HEX_DIGIT( \
|
||||
ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \
|
||||
ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE))
|
||||
|
||||
@@ -71,9 +71,23 @@ canConvertNumber(TIn) {
|
||||
}
|
||||
|
||||
// int32 -> uint32
|
||||
// int32 -> uint64
|
||||
template <typename TOut, typename TIn>
|
||||
typename enable_if<is_integral<TIn>::value && is_signed<TIn>::value &&
|
||||
is_integral<TOut>::value && is_unsigned<TOut>::value,
|
||||
is_integral<TOut>::value && is_unsigned<TOut>::value &&
|
||||
sizeof(TOut) >= sizeof(TIn),
|
||||
bool>::type
|
||||
canConvertNumber(TIn value) {
|
||||
if (value < 0)
|
||||
return false;
|
||||
return TOut(value) <= numeric_limits<TOut>::highest();
|
||||
}
|
||||
|
||||
// int32 -> uint16
|
||||
template <typename TOut, typename TIn>
|
||||
typename enable_if<is_integral<TIn>::value && is_signed<TIn>::value &&
|
||||
is_integral<TOut>::value && is_unsigned<TOut>::value &&
|
||||
sizeof(TOut) < sizeof(TIn),
|
||||
bool>::type
|
||||
canConvertNumber(TIn value) {
|
||||
if (value < 0)
|
||||
|
||||
@@ -187,8 +187,8 @@ class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
|
||||
return _object.getOrAddMember(_key);
|
||||
}
|
||||
|
||||
friend bool convertToJson(const this_type &src, VariantRef dst) {
|
||||
return dst.set(src.getUpstreamMember());
|
||||
friend void convertToJson(const this_type &src, VariantRef dst) {
|
||||
dst.set(src.getUpstreamMember());
|
||||
}
|
||||
|
||||
TObject _object;
|
||||
|
||||
@@ -239,8 +239,8 @@ class ObjectRef : public ObjectRefBase<CollectionData>,
|
||||
|
||||
template <>
|
||||
struct Converter<ObjectConstRef> {
|
||||
static bool toJson(VariantConstRef src, VariantRef dst) {
|
||||
return variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
static void toJson(VariantConstRef src, VariantRef dst) {
|
||||
variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
}
|
||||
|
||||
static ObjectConstRef fromJson(VariantConstRef src) {
|
||||
@@ -255,8 +255,8 @@ struct Converter<ObjectConstRef> {
|
||||
|
||||
template <>
|
||||
struct Converter<ObjectRef> {
|
||||
static bool toJson(VariantConstRef src, VariantRef dst) {
|
||||
return variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
static void toJson(VariantConstRef src, VariantRef dst) {
|
||||
variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
}
|
||||
|
||||
static ObjectRef fromJson(VariantRef src) {
|
||||
@@ -265,6 +265,9 @@ struct Converter<ObjectRef> {
|
||||
return ObjectRef(pool, data != 0 ? data->asObject() : 0);
|
||||
}
|
||||
|
||||
static InvalidConversion<VariantConstRef, ObjectRef> fromJson(
|
||||
VariantConstRef);
|
||||
|
||||
static bool checkJson(VariantConstRef) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -20,5 +20,6 @@
|
||||
#include "type_traits/is_signed.hpp"
|
||||
#include "type_traits/is_unsigned.hpp"
|
||||
#include "type_traits/make_unsigned.hpp"
|
||||
#include "type_traits/make_void.hpp"
|
||||
#include "type_traits/remove_const.hpp"
|
||||
#include "type_traits/remove_reference.hpp"
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "integral_constant.hpp"
|
||||
#include "is_same.hpp"
|
||||
#include "remove_cv.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename>
|
||||
struct is_floating_point : false_type {};
|
||||
template <class T>
|
||||
struct is_floating_point
|
||||
: integral_constant<
|
||||
bool, //
|
||||
is_same<float, typename remove_cv<T>::type>::value ||
|
||||
is_same<double, typename remove_cv<T>::type>::value> {};
|
||||
|
||||
template <>
|
||||
struct is_floating_point<float> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_floating_point<double> : true_type {};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -5,29 +5,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
|
||||
#include "integral_constant.hpp"
|
||||
#include "is_same.hpp"
|
||||
#include "remove_cv.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
// A meta-function that returns true if T is an integral type.
|
||||
// clang-format off
|
||||
template <typename T>
|
||||
struct is_integral {
|
||||
static const bool value =
|
||||
is_same<T, signed char>::value || is_same<T, unsigned char>::value ||
|
||||
is_same<T, signed short>::value || is_same<T, unsigned short>::value ||
|
||||
is_same<T, signed int>::value || is_same<T, unsigned int>::value ||
|
||||
is_same<T, signed long>::value || is_same<T, unsigned long>::value ||
|
||||
struct is_integral : integral_constant<bool,
|
||||
is_same<typename remove_cv<T>::type, signed char>::value ||
|
||||
is_same<typename remove_cv<T>::type, unsigned char>::value ||
|
||||
is_same<typename remove_cv<T>::type, signed short>::value ||
|
||||
is_same<typename remove_cv<T>::type, unsigned short>::value ||
|
||||
is_same<typename remove_cv<T>::type, signed int>::value ||
|
||||
is_same<typename remove_cv<T>::type, unsigned int>::value ||
|
||||
is_same<typename remove_cv<T>::type, signed long>::value ||
|
||||
is_same<typename remove_cv<T>::type, unsigned long>::value ||
|
||||
#if ARDUINOJSON_HAS_LONG_LONG
|
||||
is_same<T, signed long long>::value ||
|
||||
is_same<T, unsigned long long>::value ||
|
||||
is_same<typename remove_cv<T>::type, signed long long>::value ||
|
||||
is_same<typename remove_cv<T>::type, unsigned long long>::value ||
|
||||
#endif
|
||||
#if ARDUINOJSON_HAS_INT64
|
||||
is_same<T, signed __int64>::value ||
|
||||
is_same<T, unsigned __int64>::value ||
|
||||
is_same<typename remove_cv<T>::type, signed __int64>::value ||
|
||||
is_same<typename remove_cv<T>::type, unsigned __int64>::value ||
|
||||
#endif
|
||||
is_same<T, char>::value || is_same<T, bool>::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_integral<const T> : is_integral<T> {};
|
||||
is_same<typename remove_cv<T>::type, char>::value ||
|
||||
is_same<typename remove_cv<T>::type, bool>::value> {};
|
||||
// clang-format on
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -5,39 +5,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "integral_constant.hpp"
|
||||
#include "is_same.hpp"
|
||||
#include "remove_cv.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename>
|
||||
struct is_signed : false_type {};
|
||||
|
||||
template <>
|
||||
struct is_signed<char> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_signed<signed char> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_signed<signed short> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_signed<signed int> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_signed<signed long> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_signed<float> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_signed<double> : true_type {};
|
||||
|
||||
// clang-format off
|
||||
template <typename T>
|
||||
struct is_signed : integral_constant<bool,
|
||||
is_same<typename remove_cv<T>::type, char>::value ||
|
||||
is_same<typename remove_cv<T>::type, signed char>::value ||
|
||||
is_same<typename remove_cv<T>::type, signed short>::value ||
|
||||
is_same<typename remove_cv<T>::type, signed int>::value ||
|
||||
is_same<typename remove_cv<T>::type, signed long>::value ||
|
||||
#if ARDUINOJSON_HAS_LONG_LONG
|
||||
template <>
|
||||
struct is_signed<signed long long> : true_type {};
|
||||
is_same<typename remove_cv<T>::type, signed long long>::value ||
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_HAS_INT64
|
||||
template <>
|
||||
struct is_signed<signed __int64> : true_type {};
|
||||
is_same<typename remove_cv<T>::type, signed __int64>::value ||
|
||||
#endif
|
||||
is_same<typename remove_cv<T>::type, float>::value ||
|
||||
is_same<typename remove_cv<T>::type, double>::value> {};
|
||||
// clang-format on
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -5,33 +5,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "integral_constant.hpp"
|
||||
#include "is_same.hpp"
|
||||
#include "remove_cv.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename>
|
||||
struct is_unsigned : false_type {};
|
||||
|
||||
template <>
|
||||
struct is_unsigned<bool> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_unsigned<unsigned char> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_unsigned<unsigned short> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_unsigned<unsigned int> : true_type {};
|
||||
|
||||
template <>
|
||||
struct is_unsigned<unsigned long> : true_type {};
|
||||
|
||||
// clang-format off
|
||||
template <typename T>
|
||||
struct is_unsigned : integral_constant<bool,
|
||||
is_same<typename remove_cv<T>::type, unsigned char>::value ||
|
||||
is_same<typename remove_cv<T>::type, unsigned short>::value ||
|
||||
is_same<typename remove_cv<T>::type, unsigned int>::value ||
|
||||
is_same<typename remove_cv<T>::type, unsigned long>::value ||
|
||||
#if ARDUINOJSON_HAS_INT64
|
||||
template <>
|
||||
struct is_unsigned<unsigned __int64> : true_type {};
|
||||
is_same<typename remove_cv<T>::type, unsigned __int64>::value ||
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_HAS_LONG_LONG
|
||||
template <>
|
||||
struct is_unsigned<unsigned long long> : true_type {};
|
||||
is_same<typename remove_cv<T>::type, unsigned long long>::value ||
|
||||
#endif
|
||||
is_same<typename remove_cv<T>::type, bool>::value> {};
|
||||
// clang-format on
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <class = void>
|
||||
struct make_void {
|
||||
typedef void type;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,27 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename T>
|
||||
struct remove_cv {
|
||||
typedef T type;
|
||||
};
|
||||
template <typename T>
|
||||
struct remove_cv<const T> {
|
||||
typedef T type;
|
||||
};
|
||||
template <typename T>
|
||||
struct remove_cv<volatile T> {
|
||||
typedef T type;
|
||||
};
|
||||
template <typename T>
|
||||
struct remove_cv<const volatile T> {
|
||||
typedef T type;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -22,10 +22,10 @@ class Writer< ::String, void> {
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) {
|
||||
ARDUINOJSON_ASSERT(_size < bufferCapacity);
|
||||
_buffer[_size++] = static_cast<char>(c);
|
||||
if (_size + 1 >= bufferCapacity)
|
||||
flush();
|
||||
if (flush() != 0)
|
||||
return 0;
|
||||
_buffer[_size++] = static_cast<char>(c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -36,14 +36,15 @@ class Writer< ::String, void> {
|
||||
return n;
|
||||
}
|
||||
|
||||
private:
|
||||
void flush() {
|
||||
size_t flush() {
|
||||
ARDUINOJSON_ASSERT(_size < bufferCapacity);
|
||||
_buffer[_size] = 0;
|
||||
*_destination += _buffer;
|
||||
if (_destination->concat(_buffer))
|
||||
_size = 0;
|
||||
return _size;
|
||||
}
|
||||
|
||||
private:
|
||||
::String *_destination;
|
||||
char _buffer[bufferCapacity];
|
||||
size_t _size;
|
||||
|
||||
@@ -55,8 +55,12 @@ class StringCopier {
|
||||
|
||||
private:
|
||||
MemoryPool* _pool;
|
||||
|
||||
// These fields aren't initialized by the constructor but startString()
|
||||
//
|
||||
// NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
|
||||
char* _ptr;
|
||||
size_t _size;
|
||||
size_t _capacity;
|
||||
// NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
|
||||
size_t _size, _capacity;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -7,14 +7,15 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <ArduinoJson/Polyfills/safe_strcmp.hpp>
|
||||
#include <ArduinoJson/Strings/IsString.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapter.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class ArduinoStringAdapter {
|
||||
template <>
|
||||
class StringAdapter< ::String> {
|
||||
public:
|
||||
ArduinoStringAdapter(const ::String& str) : _str(&str) {}
|
||||
StringAdapter(const ::String& str) : _str(&str) {}
|
||||
|
||||
void copyTo(char* p, size_t n) const {
|
||||
memcpy(p, _str->c_str(), n);
|
||||
@@ -31,18 +32,10 @@ class ArduinoStringAdapter {
|
||||
return safe_strcmp(me, other);
|
||||
}
|
||||
|
||||
bool equals(const char* expected) const {
|
||||
return compare(expected) == 0;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return _str->length();
|
||||
}
|
||||
|
||||
const char* begin() const {
|
||||
return _str->c_str();
|
||||
}
|
||||
|
||||
typedef storage_policies::store_by_copy storage_policy;
|
||||
|
||||
private:
|
||||
@@ -50,13 +43,10 @@ class ArduinoStringAdapter {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct IsString< ::String> : true_type {};
|
||||
|
||||
template <>
|
||||
struct IsString< ::StringSumHelper> : true_type {};
|
||||
|
||||
inline ArduinoStringAdapter adaptString(const ::String& str) {
|
||||
return ArduinoStringAdapter(str);
|
||||
}
|
||||
class StringAdapter< ::StringSumHelper> : public StringAdapter< ::String> {
|
||||
public:
|
||||
StringAdapter< ::StringSumHelper>(const ::String& s)
|
||||
: StringAdapter< ::String>(s) {}
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -8,23 +8,20 @@
|
||||
#include <string.h> // strcmp
|
||||
|
||||
#include <ArduinoJson/Polyfills/safe_strcmp.hpp>
|
||||
#include <ArduinoJson/Strings/IsString.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapter.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class ConstRamStringAdapter {
|
||||
template <>
|
||||
class StringAdapter<const char*> {
|
||||
public:
|
||||
ConstRamStringAdapter(const char* str = 0) : _str(str) {}
|
||||
StringAdapter(const char* str = 0) : _str(str) {}
|
||||
|
||||
int compare(const char* other) const {
|
||||
return safe_strcmp(_str, other);
|
||||
}
|
||||
|
||||
bool equals(const char* expected) const {
|
||||
return compare(expected) == 0;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return !_str;
|
||||
}
|
||||
@@ -39,24 +36,16 @@ class ConstRamStringAdapter {
|
||||
return _str;
|
||||
}
|
||||
|
||||
const char* begin() const {
|
||||
return _str;
|
||||
}
|
||||
|
||||
typedef storage_policies::store_by_address storage_policy;
|
||||
|
||||
protected:
|
||||
const char* _str;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct IsString<const char*> : true_type {};
|
||||
|
||||
template <int N>
|
||||
struct IsString<const char[N]> : true_type {};
|
||||
|
||||
inline ConstRamStringAdapter adaptString(const char* str) {
|
||||
return ConstRamStringAdapter(str);
|
||||
}
|
||||
class StringAdapter<const char[N]> : public StringAdapter<const char*> {
|
||||
public:
|
||||
StringAdapter<const char[N]>(const char* s) : StringAdapter<const char*>(s) {}
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -5,15 +5,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Polyfills/pgmspace.hpp>
|
||||
#include <ArduinoJson/Strings/FlashStringIterator.hpp>
|
||||
#include <ArduinoJson/Strings/IsString.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapter.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class FlashStringAdapter {
|
||||
template <>
|
||||
class StringAdapter<const __FlashStringHelper*> {
|
||||
public:
|
||||
FlashStringAdapter(const __FlashStringHelper* str) : _str(str) {}
|
||||
StringAdapter(const __FlashStringHelper* str) : _str(str) {}
|
||||
|
||||
int compare(const char* other) const {
|
||||
if (!other && !_str)
|
||||
@@ -25,10 +25,6 @@ class FlashStringAdapter {
|
||||
return -strcmp_P(other, reinterpret_cast<const char*>(_str));
|
||||
}
|
||||
|
||||
bool equals(const char* expected) const {
|
||||
return compare(expected) == 0;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return !_str;
|
||||
}
|
||||
@@ -43,20 +39,10 @@ class FlashStringAdapter {
|
||||
return strlen_P(reinterpret_cast<const char*>(_str));
|
||||
}
|
||||
|
||||
FlashStringIterator begin() const {
|
||||
return FlashStringIterator(_str);
|
||||
}
|
||||
|
||||
typedef storage_policies::store_by_copy storage_policy;
|
||||
|
||||
private:
|
||||
const __FlashStringHelper* _str;
|
||||
};
|
||||
|
||||
inline FlashStringAdapter adaptString(const __FlashStringHelper* str) {
|
||||
return FlashStringAdapter(str);
|
||||
}
|
||||
|
||||
template <>
|
||||
struct IsString<const __FlashStringHelper*> : true_type {};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,27 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Strings/Adapters/RamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/String.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <>
|
||||
class StringAdapter<String> : public StringAdapter<char*> {
|
||||
public:
|
||||
StringAdapter(const String& str)
|
||||
: StringAdapter<char*>(str.c_str()), _isStatic(str.isStatic()) {}
|
||||
|
||||
bool isStatic() const {
|
||||
return _isStatic;
|
||||
}
|
||||
|
||||
typedef storage_policies::decide_at_runtime storage_policy;
|
||||
|
||||
private:
|
||||
bool _isStatic;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,29 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapter.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TChar>
|
||||
class StringAdapter<TChar*, false,
|
||||
typename enable_if<sizeof(TChar) == 1 &&
|
||||
!is_same<TChar, void>::value>::type>
|
||||
: public StringAdapter<const char*> {
|
||||
public:
|
||||
StringAdapter(const TChar* str)
|
||||
: StringAdapter<const char*>(reinterpret_cast<const char*>(str)) {}
|
||||
|
||||
void copyTo(char* p, size_t n) const {
|
||||
memcpy(p, _str, n);
|
||||
}
|
||||
|
||||
typedef ARDUINOJSON_NAMESPACE::storage_policies::store_by_copy storage_policy;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -5,15 +5,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Strings/FlashStringIterator.hpp>
|
||||
#include <ArduinoJson/Strings/IsString.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapter.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class SizedFlashStringAdapter {
|
||||
template <>
|
||||
class StringAdapter<const __FlashStringHelper*, true> {
|
||||
public:
|
||||
SizedFlashStringAdapter(const __FlashStringHelper* str, size_t sz)
|
||||
StringAdapter(const __FlashStringHelper* str, size_t sz)
|
||||
: _str(str), _size(sz) {}
|
||||
|
||||
int compare(const char* other) const {
|
||||
@@ -26,10 +26,6 @@ class SizedFlashStringAdapter {
|
||||
return -strncmp_P(other, reinterpret_cast<const char*>(_str), _size);
|
||||
}
|
||||
|
||||
bool equals(const char* expected) const {
|
||||
return compare(expected) == 0;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return !_str;
|
||||
}
|
||||
@@ -42,10 +38,6 @@ class SizedFlashStringAdapter {
|
||||
return _size;
|
||||
}
|
||||
|
||||
FlashStringIterator begin() const {
|
||||
return FlashStringIterator(_str);
|
||||
}
|
||||
|
||||
typedef storage_policies::store_by_copy storage_policy;
|
||||
|
||||
private:
|
||||
@@ -53,8 +45,4 @@ class SizedFlashStringAdapter {
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
inline SizedFlashStringAdapter adaptString(const __FlashStringHelper* str,
|
||||
size_t sz) {
|
||||
return SizedFlashStringAdapter(str, sz);
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -5,25 +5,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Strings/IsString.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapter.hpp>
|
||||
|
||||
#include <string.h> // strcmp
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class SizedRamStringAdapter {
|
||||
template <typename TChar>
|
||||
class StringAdapter<TChar*, true> {
|
||||
public:
|
||||
SizedRamStringAdapter(const char* str, size_t n) : _str(str), _size(n) {}
|
||||
StringAdapter(const char* str, size_t n) : _str(str), _size(n) {}
|
||||
|
||||
int compare(const char* other) const {
|
||||
return safe_strncmp(_str, other, _size);
|
||||
}
|
||||
|
||||
bool equals(const char* expected) const {
|
||||
return compare(expected) == 0;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return !_str;
|
||||
}
|
||||
@@ -36,10 +33,6 @@ class SizedRamStringAdapter {
|
||||
return _size;
|
||||
}
|
||||
|
||||
const char* begin() const {
|
||||
return _str;
|
||||
}
|
||||
|
||||
typedef storage_policies::store_by_copy storage_policy;
|
||||
|
||||
private:
|
||||
@@ -47,9 +40,4 @@ class SizedRamStringAdapter {
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
template <typename TChar>
|
||||
inline SizedRamStringAdapter adaptString(const TChar* str, size_t size) {
|
||||
return SizedRamStringAdapter(reinterpret_cast<const char*>(str), size);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,46 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapter.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TCharTraits, typename TAllocator>
|
||||
class StringAdapter<std::basic_string<char, TCharTraits, TAllocator> > {
|
||||
public:
|
||||
typedef std::basic_string<char, TCharTraits, TAllocator> string_type;
|
||||
|
||||
StringAdapter(const string_type& str) : _str(&str) {}
|
||||
|
||||
void copyTo(char* p, size_t n) const {
|
||||
memcpy(p, _str->c_str(), n);
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
int compare(const char* other) const {
|
||||
if (!other)
|
||||
return 1;
|
||||
return _str->compare(other);
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return _str->size();
|
||||
}
|
||||
|
||||
typedef storage_policies::store_by_copy storage_policy;
|
||||
|
||||
private:
|
||||
const string_type* _str;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,44 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapter.hpp>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <>
|
||||
class StringAdapter<std::string_view> {
|
||||
public:
|
||||
StringAdapter(std::string_view str) : _str(str) {}
|
||||
|
||||
void copyTo(char* p, size_t n) const {
|
||||
memcpy(p, _str.data(), n);
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
int compare(const char* other) const {
|
||||
if (!other)
|
||||
return 1;
|
||||
return _str.compare(other);
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return _str.size();
|
||||
}
|
||||
|
||||
typedef storage_policies::store_by_copy storage_policy;
|
||||
|
||||
private:
|
||||
std::string_view _str;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -1,44 +0,0 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class FlashStringIterator {
|
||||
public:
|
||||
explicit FlashStringIterator(const __FlashStringHelper* ptr)
|
||||
: _ptr(reinterpret_cast<const char*>(ptr)) {}
|
||||
|
||||
explicit FlashStringIterator(const char* ptr) : _ptr(ptr) {}
|
||||
|
||||
FlashStringIterator operator+(ptrdiff_t d) const {
|
||||
return FlashStringIterator(_ptr + d);
|
||||
}
|
||||
|
||||
ptrdiff_t operator-(FlashStringIterator other) const {
|
||||
return _ptr - other._ptr;
|
||||
}
|
||||
|
||||
FlashStringIterator operator++(int) {
|
||||
return FlashStringIterator(_ptr++);
|
||||
}
|
||||
|
||||
FlashStringIterator operator++() {
|
||||
return FlashStringIterator(++_ptr);
|
||||
}
|
||||
|
||||
bool operator!=(FlashStringIterator other) const {
|
||||
return _ptr != other._ptr;
|
||||
}
|
||||
|
||||
char operator*() const {
|
||||
return char(pgm_read_byte(_ptr));
|
||||
}
|
||||
|
||||
private:
|
||||
const char* _ptr;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -1,18 +0,0 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
template <typename>
|
||||
struct IsString : false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct IsString<const T> : IsString<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct IsString<T&> : IsString<T> {};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -1,43 +0,0 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Strings/ConstRamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/IsString.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class RamStringAdapter : public ConstRamStringAdapter {
|
||||
public:
|
||||
RamStringAdapter(const char* str) : ConstRamStringAdapter(str) {}
|
||||
|
||||
void copyTo(char* p, size_t n) const {
|
||||
memcpy(p, _str, n);
|
||||
}
|
||||
|
||||
typedef ARDUINOJSON_NAMESPACE::storage_policies::store_by_copy storage_policy;
|
||||
};
|
||||
|
||||
template <typename TChar>
|
||||
inline RamStringAdapter adaptString(const TChar* str) {
|
||||
return RamStringAdapter(reinterpret_cast<const char*>(str));
|
||||
}
|
||||
|
||||
inline RamStringAdapter adaptString(char* str) {
|
||||
return RamStringAdapter(str);
|
||||
}
|
||||
|
||||
template <typename TChar>
|
||||
struct IsString<TChar*> {
|
||||
static const bool value = sizeof(TChar) == 1;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct IsString<void*> {
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -1,65 +0,0 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Strings/IsString.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TString>
|
||||
class StdStringAdapter {
|
||||
public:
|
||||
StdStringAdapter(const TString& str) : _str(&str) {}
|
||||
|
||||
void copyTo(char* p, size_t n) const {
|
||||
memcpy(p, _str->c_str(), n);
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
int compare(const char* other) const {
|
||||
if (!other)
|
||||
return 1;
|
||||
return _str->compare(other);
|
||||
}
|
||||
|
||||
bool equals(const char* expected) const {
|
||||
if (!expected)
|
||||
return false;
|
||||
return *_str == expected;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return _str->size();
|
||||
}
|
||||
|
||||
const char* begin() const {
|
||||
return _str->c_str();
|
||||
}
|
||||
|
||||
typedef storage_policies::store_by_copy storage_policy;
|
||||
|
||||
private:
|
||||
const TString* _str;
|
||||
};
|
||||
|
||||
template <typename TCharTraits, typename TAllocator>
|
||||
struct IsString<std::basic_string<char, TCharTraits, TAllocator> > : true_type {
|
||||
};
|
||||
|
||||
template <typename TCharTraits, typename TAllocator>
|
||||
inline StdStringAdapter<std::basic_string<char, TCharTraits, TAllocator> >
|
||||
adaptString(const std::basic_string<char, TCharTraits, TAllocator>& str) {
|
||||
return StdStringAdapter<std::basic_string<char, TCharTraits, TAllocator> >(
|
||||
str);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -4,10 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Strings/ConstRamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/IsString.hpp>
|
||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class String {
|
||||
@@ -53,25 +49,4 @@ class String {
|
||||
bool _isStatic;
|
||||
};
|
||||
|
||||
class StringAdapter : public RamStringAdapter {
|
||||
public:
|
||||
StringAdapter(const String& str)
|
||||
: RamStringAdapter(str.c_str()), _isStatic(str.isStatic()) {}
|
||||
|
||||
bool isStatic() const {
|
||||
return _isStatic;
|
||||
}
|
||||
|
||||
typedef storage_policies::decide_at_runtime storage_policy;
|
||||
|
||||
private:
|
||||
bool _isStatic;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct IsString<String> : true_type {};
|
||||
|
||||
inline StringAdapter adaptString(const String& str) {
|
||||
return StringAdapter(str);
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
32
lib/ArduinoJson/src/ArduinoJson/Strings/StringAdapter.hpp
Normal file
32
lib/ArduinoJson/src/ArduinoJson/Strings/StringAdapter.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename T, bool bounded = false, typename Enable = void>
|
||||
class StringAdapter;
|
||||
|
||||
template <typename T>
|
||||
inline StringAdapter<T, false> adaptString(const T& str) {
|
||||
return StringAdapter<T, false>(str);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline StringAdapter<T, true> adaptString(const T& str, size_t sz) {
|
||||
return StringAdapter<T, true>(str, sz);
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct IsString : false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct IsString<
|
||||
T, typename make_void<typename StringAdapter<T>::storage_policy>::type>
|
||||
: true_type {};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -4,19 +4,24 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Strings/ConstRamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/RamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/SizedRamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/Adapters/ConstRamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/Adapters/JsonStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/Adapters/RamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/Adapters/SizedRamStringAdapter.hpp>
|
||||
|
||||
#if ARDUINOJSON_ENABLE_STD_STRING
|
||||
#include <ArduinoJson/Strings/StdStringAdapter.hpp>
|
||||
# include <ArduinoJson/Strings/Adapters/StdStringAdapter.hpp>
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_ENABLE_STRING_VIEW
|
||||
# include <ArduinoJson/Strings/Adapters/StringViewAdapter.hpp>
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_ENABLE_ARDUINO_STRING
|
||||
#include <ArduinoJson/Strings/ArduinoStringAdapter.hpp>
|
||||
# include <ArduinoJson/Strings/Adapters/ArduinoStringAdapter.hpp>
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_ENABLE_PROGMEM
|
||||
#include <ArduinoJson/Strings/FlashStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/SizedFlashStringAdapter.hpp>
|
||||
# include <ArduinoJson/Strings/Adapters/FlashStringAdapter.hpp>
|
||||
# include <ArduinoJson/Strings/Adapters/SizedFlashStringAdapter.hpp>
|
||||
#endif
|
||||
|
||||
@@ -9,4 +9,9 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
template <typename T, typename Enable = void>
|
||||
struct Converter;
|
||||
|
||||
// clang-format off
|
||||
template <typename T1, typename T2>
|
||||
class InvalidConversion; // Error here? See https://arduinojson.org/v6/invalid-conversion/
|
||||
// clang-format on
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename T, typename Enable>
|
||||
struct Converter {
|
||||
static bool toJson(const T& src, VariantRef dst) {
|
||||
static void toJson(const T& src, VariantRef dst) {
|
||||
// clang-format off
|
||||
return convertToJson(src, dst); // Error here? See https://arduinojson.org/v6/unsupported-set/
|
||||
convertToJson(src, dst); // Error here? See https://arduinojson.org/v6/unsupported-set/
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
@@ -38,13 +38,11 @@ template <typename T>
|
||||
struct Converter<
|
||||
T, typename enable_if<is_integral<T>::value && !is_same<bool, T>::value &&
|
||||
!is_same<char, T>::value>::type> {
|
||||
static bool toJson(T src, VariantRef dst) {
|
||||
static void toJson(T src, VariantRef dst) {
|
||||
VariantData* data = getData(dst);
|
||||
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
|
||||
if (!data)
|
||||
return false;
|
||||
if (data)
|
||||
data->setInteger(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
static T fromJson(VariantConstRef src) {
|
||||
@@ -61,8 +59,8 @@ struct Converter<
|
||||
|
||||
template <typename T>
|
||||
struct Converter<T, typename enable_if<is_enum<T>::value>::type> {
|
||||
static bool toJson(T src, VariantRef dst) {
|
||||
return dst.set(static_cast<Integer>(src));
|
||||
static void toJson(T src, VariantRef dst) {
|
||||
dst.set(static_cast<Integer>(src));
|
||||
}
|
||||
|
||||
static T fromJson(VariantConstRef src) {
|
||||
@@ -78,12 +76,10 @@ struct Converter<T, typename enable_if<is_enum<T>::value>::type> {
|
||||
|
||||
template <>
|
||||
struct Converter<bool> {
|
||||
static bool toJson(bool src, VariantRef dst) {
|
||||
static void toJson(bool src, VariantRef dst) {
|
||||
VariantData* data = getData(dst);
|
||||
if (!data)
|
||||
return false;
|
||||
if (data)
|
||||
data->setBoolean(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fromJson(VariantConstRef src) {
|
||||
@@ -99,12 +95,10 @@ struct Converter<bool> {
|
||||
|
||||
template <typename T>
|
||||
struct Converter<T, typename enable_if<is_floating_point<T>::value>::type> {
|
||||
static bool toJson(T src, VariantRef dst) {
|
||||
static void toJson(T src, VariantRef dst) {
|
||||
VariantData* data = getData(dst);
|
||||
if (!data)
|
||||
return false;
|
||||
if (data)
|
||||
data->setFloat(static_cast<Float>(src));
|
||||
return true;
|
||||
}
|
||||
|
||||
static T fromJson(VariantConstRef src) {
|
||||
@@ -120,8 +114,8 @@ struct Converter<T, typename enable_if<is_floating_point<T>::value>::type> {
|
||||
|
||||
template <>
|
||||
struct Converter<const char*> {
|
||||
static bool toJson(const char* src, VariantRef dst) {
|
||||
return variantSetString(getData(dst), adaptString(src), getPool(dst));
|
||||
static void toJson(const char* src, VariantRef dst) {
|
||||
variantSetString(getData(dst), adaptString(src), getPool(dst));
|
||||
}
|
||||
|
||||
static const char* fromJson(VariantConstRef src) {
|
||||
@@ -163,12 +157,10 @@ canConvertFromJson(VariantConstRef src, const T&) {
|
||||
|
||||
template <>
|
||||
struct Converter<SerializedValue<const char*> > {
|
||||
static bool toJson(SerializedValue<const char*> src, VariantRef dst) {
|
||||
static void toJson(SerializedValue<const char*> src, VariantRef dst) {
|
||||
VariantData* data = getData(dst);
|
||||
if (!data)
|
||||
return false;
|
||||
if (data)
|
||||
data->setLinkedRaw(src);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -178,10 +170,11 @@ struct Converter<SerializedValue<const char*> > {
|
||||
template <typename T>
|
||||
struct Converter<SerializedValue<T>,
|
||||
typename enable_if<!is_same<const char*, T>::value>::type> {
|
||||
static bool toJson(SerializedValue<T> src, VariantRef dst) {
|
||||
static void toJson(SerializedValue<T> src, VariantRef dst) {
|
||||
VariantData* data = getData(dst);
|
||||
MemoryPool* pool = getPool(dst);
|
||||
return data != 0 && data->setOwnedRaw(src, pool);
|
||||
if (data)
|
||||
data->setOwnedRaw(src, pool);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -189,9 +182,8 @@ struct Converter<SerializedValue<T>,
|
||||
|
||||
template <>
|
||||
struct Converter<decltype(nullptr)> {
|
||||
static bool toJson(decltype(nullptr), VariantRef dst) {
|
||||
static void toJson(decltype(nullptr), VariantRef dst) {
|
||||
variantSetNull(getData(dst));
|
||||
return true;
|
||||
}
|
||||
static decltype(nullptr) fromJson(VariantConstRef) {
|
||||
return nullptr;
|
||||
@@ -247,20 +239,33 @@ class MemoryPoolPrint : public Print {
|
||||
size_t _capacity;
|
||||
};
|
||||
|
||||
inline bool convertToJson(const ::Printable& src, VariantRef dst) {
|
||||
inline void convertToJson(const ::Printable& src, VariantRef dst) {
|
||||
MemoryPool* pool = getPool(dst);
|
||||
VariantData* data = getData(dst);
|
||||
if (!pool || !data)
|
||||
return false;
|
||||
return;
|
||||
MemoryPoolPrint print(pool);
|
||||
src.printTo(print);
|
||||
if (print.overflowed()) {
|
||||
pool->markAsOverflowed();
|
||||
data->setNull();
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
data->setStringPointer(print.c_str(), storage_policies::store_by_copy());
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_ENABLE_STRING_VIEW
|
||||
|
||||
inline void convertFromJson(VariantConstRef src, std::string_view& dst) {
|
||||
const char* str = src.as<const char*>();
|
||||
if (str) // the standard doesn't allow passing null to the constructor
|
||||
dst = std::string_view(str);
|
||||
}
|
||||
|
||||
inline bool canConvertFromJson(VariantConstRef src, const std::string_view&) {
|
||||
return src.is<const char*>();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <ArduinoJson/Misc/Visitable.hpp>
|
||||
#include <ArduinoJson/Numbers/arithmeticCompare.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
#include <ArduinoJson/Strings/IsString.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapter.hpp>
|
||||
#include <ArduinoJson/Variant/Visitor.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <ArduinoJson/Misc/SerializedValue.hpp>
|
||||
#include <ArduinoJson/Numbers/convertNumber.hpp>
|
||||
#include <ArduinoJson/Strings/RamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <ArduinoJson/Variant/VariantContent.hpp>
|
||||
|
||||
// VariantData can't have a constructor (to be a POD), so we have no way to fix
|
||||
@@ -33,7 +33,7 @@ class VariantData {
|
||||
// - no virtual
|
||||
// - no inheritance
|
||||
void init() {
|
||||
_flags = 0;
|
||||
_flags = VALUE_IS_NULL;
|
||||
}
|
||||
|
||||
template <typename TVisitor>
|
||||
@@ -103,7 +103,8 @@ class VariantData {
|
||||
case VALUE_IS_OBJECT:
|
||||
return toObject().copyFrom(src._content.asCollection, pool);
|
||||
case VALUE_IS_OWNED_STRING:
|
||||
return setString(RamStringAdapter(src._content.asString), pool);
|
||||
return setString(adaptString(const_cast<char *>(src._content.asString)),
|
||||
pool);
|
||||
case VALUE_IS_OWNED_RAW:
|
||||
return setOwnedRaw(
|
||||
serialized(src._content.asRaw.data, src._content.asRaw.size), pool);
|
||||
|
||||
@@ -140,4 +140,9 @@ inline VariantConstRef operator|(VariantConstRef preferedValue,
|
||||
VariantConstRef defaultValue) {
|
||||
return preferedValue ? preferedValue : defaultValue;
|
||||
}
|
||||
|
||||
// Out of class definition to avoid #1560
|
||||
inline bool VariantRef::set(char value) const {
|
||||
return set<signed char>(value);
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
@@ -85,18 +85,18 @@ class VariantRef : public VariantRefBase<VariantData>,
|
||||
|
||||
template <typename T>
|
||||
FORCE_INLINE bool set(const T &value) const {
|
||||
return Converter<T>::toJson(value, *this);
|
||||
Converter<T>::toJson(value, *this);
|
||||
return _pool && !_pool->overflowed();
|
||||
}
|
||||
|
||||
FORCE_INLINE bool ARDUINOJSON_DEPRECATED(
|
||||
bool ARDUINOJSON_DEPRECATED(
|
||||
"Support for char is deprecated, use int8_t or uint8_t instead")
|
||||
set(char value) const {
|
||||
return set<signed char>(value);
|
||||
}
|
||||
set(char value) const;
|
||||
|
||||
template <typename T>
|
||||
FORCE_INLINE bool set(T *value) const {
|
||||
return Converter<T *>::toJson(value, *this);
|
||||
Converter<T *>::toJson(value, *this);
|
||||
return _pool && !_pool->overflowed();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -341,16 +341,22 @@ class VariantConstRef : public VariantRefBase<const VariantData>,
|
||||
|
||||
template <>
|
||||
struct Converter<VariantRef> {
|
||||
static bool toJson(VariantRef src, VariantRef dst) {
|
||||
return variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
static void toJson(VariantRef src, VariantRef dst) {
|
||||
variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
}
|
||||
|
||||
static VariantRef fromJson(VariantRef src) {
|
||||
return src;
|
||||
}
|
||||
|
||||
static InvalidConversion<VariantConstRef, VariantRef> fromJson(
|
||||
VariantConstRef);
|
||||
|
||||
static bool checkJson(VariantRef src) {
|
||||
VariantData *data = getData(src);
|
||||
return !!data;
|
||||
}
|
||||
|
||||
static bool checkJson(VariantConstRef) {
|
||||
return false;
|
||||
}
|
||||
@@ -358,8 +364,8 @@ struct Converter<VariantRef> {
|
||||
|
||||
template <>
|
||||
struct Converter<VariantConstRef> {
|
||||
static bool toJson(VariantConstRef src, VariantRef dst) {
|
||||
return variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
static void toJson(VariantConstRef src, VariantRef dst) {
|
||||
variantCopyFrom(getData(dst), getData(src), getPool(dst));
|
||||
}
|
||||
|
||||
static VariantConstRef fromJson(VariantConstRef src) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ARDUINOJSON_VERSION "6.18.0"
|
||||
#define ARDUINOJSON_VERSION "6.18.3"
|
||||
#define ARDUINOJSON_VERSION_MAJOR 6
|
||||
#define ARDUINOJSON_VERSION_MINOR 18
|
||||
#define ARDUINOJSON_VERSION_REVISION 0
|
||||
#define ARDUINOJSON_VERSION_REVISION 3
|
||||
|
||||
@@ -36,7 +36,15 @@ extern "C"{
|
||||
* */
|
||||
|
||||
typedef enum {
|
||||
LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_FIN, LWIP_TCP_ERROR, LWIP_TCP_POLL, LWIP_TCP_CLEAR, LWIP_TCP_ACCEPT, LWIP_TCP_CONNECTED, LWIP_TCP_DNS
|
||||
LWIP_TCP_SENT,
|
||||
LWIP_TCP_RECV,
|
||||
LWIP_TCP_FIN,
|
||||
LWIP_TCP_ERROR,
|
||||
LWIP_TCP_POLL,
|
||||
LWIP_TCP_CLEAR,
|
||||
LWIP_TCP_ACCEPT,
|
||||
LWIP_TCP_CONNECTED,
|
||||
LWIP_TCP_DNS
|
||||
} lwip_event_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -563,8 +571,7 @@ AsyncClient::AsyncClient(tcp_pcb* pcb)
|
||||
, _ack_timeout(ASYNC_MAX_ACK_TIME)
|
||||
, _connect_port(0)
|
||||
, prev(NULL)
|
||||
, next(NULL)
|
||||
{
|
||||
, next(NULL) {
|
||||
_pcb = pcb;
|
||||
_closed_slot = -1;
|
||||
if (_pcb) {
|
||||
@@ -674,7 +681,7 @@ void AsyncClient::onPoll(AcConnectHandler cb, void* arg){
|
||||
* Main Public Methods
|
||||
* */
|
||||
|
||||
bool AsyncClient::connect(IPAddress ip, uint16_t port){
|
||||
bool AsyncClient::_connect(ip_addr_t addr, uint16_t port) {
|
||||
if (_pcb) {
|
||||
log_w("already connected, state %d", _pcb->state);
|
||||
return false;
|
||||
@@ -684,11 +691,7 @@ bool AsyncClient::connect(IPAddress ip, uint16_t port){
|
||||
return false;
|
||||
}
|
||||
|
||||
ip_addr_t addr;
|
||||
addr.type = IPADDR_TYPE_V4;
|
||||
addr.u_addr.ip4.addr = ip;
|
||||
|
||||
tcp_pcb* pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
|
||||
tcp_pcb * pcb = tcp_new_ip_type(addr.type);
|
||||
if (!pcb) {
|
||||
log_e("pcb == NULL");
|
||||
return false;
|
||||
@@ -699,11 +702,26 @@ bool AsyncClient::connect(IPAddress ip, uint16_t port){
|
||||
tcp_recv(pcb, &_tcp_recv);
|
||||
tcp_sent(pcb, &_tcp_sent);
|
||||
tcp_poll(pcb, &_tcp_poll, 1);
|
||||
//_tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected);
|
||||
_tcp_connect(pcb, _closed_slot, &addr, port, (tcp_connected_fn)&_tcp_connected);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsyncClient::connect(IPAddress ip, uint16_t port) {
|
||||
ip_addr_t addr;
|
||||
addr.type = IPADDR_TYPE_V4;
|
||||
addr.u_addr.ip4.addr = ip;
|
||||
|
||||
return _connect(addr, port);
|
||||
}
|
||||
|
||||
bool AsyncClient::connect(IPv6Address ip, uint16_t port) {
|
||||
ip_addr_t addr;
|
||||
addr.type = IPADDR_TYPE_V6;
|
||||
memcpy(addr.u_addr.ip6.addr, static_cast<const uint32_t *>(ip), sizeof(uint32_t) * 4);
|
||||
|
||||
return _connect(addr, port);
|
||||
}
|
||||
|
||||
bool AsyncClient::connect(const char * host, uint16_t port) {
|
||||
ip_addr_t addr;
|
||||
|
||||
@@ -714,6 +732,9 @@ bool AsyncClient::connect(const char* host, uint16_t port){
|
||||
|
||||
err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_tcp_dns_found, this);
|
||||
if (err == ERR_OK) {
|
||||
if (addr.type == IPADDR_TYPE_V6) {
|
||||
return connect(IPv6Address(addr.u_addr.ip6.addr), port);
|
||||
}
|
||||
return connect(IPAddress(addr.u_addr.ip4.addr), port);
|
||||
} else if (err == ERR_INPROGRESS) {
|
||||
_connect_port = port;
|
||||
@@ -980,6 +1001,8 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){
|
||||
void AsyncClient::_dns_found(struct ip_addr * ipaddr) {
|
||||
if (ipaddr && ipaddr->u_addr.ip4.addr) {
|
||||
connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port);
|
||||
} else if (ipaddr && ipaddr->u_addr.ip6.addr) {
|
||||
connect(IPv6Address(ipaddr->u_addr.ip6.addr), _connect_port);
|
||||
} else {
|
||||
if (_error_cb) {
|
||||
_error_cb(_error_cb_arg, this, -55);
|
||||
@@ -1071,6 +1094,15 @@ uint32_t AsyncClient::getRemoteAddress() {
|
||||
return _pcb->remote_ip.u_addr.ip4.addr;
|
||||
}
|
||||
|
||||
ip6_addr_t AsyncClient::getRemoteAddress6() {
|
||||
if (!_pcb) {
|
||||
ip6_addr_t nulladdr;
|
||||
ip6_addr_set_zero(&nulladdr);
|
||||
return nulladdr;
|
||||
}
|
||||
return _pcb->remote_ip.u_addr.ip6;
|
||||
}
|
||||
|
||||
uint16_t AsyncClient::getRemotePort() {
|
||||
if (!_pcb) {
|
||||
return 0;
|
||||
@@ -1085,6 +1117,15 @@ uint32_t AsyncClient::getLocalAddress() {
|
||||
return _pcb->local_ip.u_addr.ip4.addr;
|
||||
}
|
||||
|
||||
ip6_addr_t AsyncClient::getLocalAddress6() {
|
||||
if (!_pcb) {
|
||||
ip6_addr_t nulladdr;
|
||||
ip6_addr_set_zero(&nulladdr);
|
||||
return nulladdr;
|
||||
}
|
||||
return _pcb->local_ip.u_addr.ip6;
|
||||
}
|
||||
|
||||
uint16_t AsyncClient::getLocalPort() {
|
||||
if (!_pcb) {
|
||||
return 0;
|
||||
@@ -1096,6 +1137,10 @@ IPAddress AsyncClient::remoteIP() {
|
||||
return IPAddress(getRemoteAddress());
|
||||
}
|
||||
|
||||
IPv6Address AsyncClient::remoteIP6() {
|
||||
return IPv6Address(getRemoteAddress6().addr);
|
||||
}
|
||||
|
||||
uint16_t AsyncClient::remotePort() {
|
||||
return getRemotePort();
|
||||
}
|
||||
@@ -1104,6 +1149,10 @@ IPAddress AsyncClient::localIP() {
|
||||
return IPAddress(getLocalAddress());
|
||||
}
|
||||
|
||||
IPv6Address AsyncClient::localIP6() {
|
||||
return IPv6Address(getLocalAddress6().addr);
|
||||
}
|
||||
|
||||
uint16_t AsyncClient::localPort() {
|
||||
return getLocalPort();
|
||||
}
|
||||
@@ -1156,41 +1205,71 @@ bool AsyncClient::canSend(){
|
||||
|
||||
const char * AsyncClient::errorToString(int8_t error) {
|
||||
switch (error) {
|
||||
case ERR_OK: return "OK";
|
||||
case ERR_MEM: return "Out of memory error";
|
||||
case ERR_BUF: return "Buffer error";
|
||||
case ERR_TIMEOUT: return "Timeout";
|
||||
case ERR_RTE: return "Routing problem";
|
||||
case ERR_INPROGRESS: return "Operation in progress";
|
||||
case ERR_VAL: return "Illegal value";
|
||||
case ERR_WOULDBLOCK: return "Operation would block";
|
||||
case ERR_USE: return "Address in use";
|
||||
case ERR_ALREADY: return "Already connected";
|
||||
case ERR_CONN: return "Not connected";
|
||||
case ERR_IF: return "Low-level netif error";
|
||||
case ERR_ABRT: return "Connection aborted";
|
||||
case ERR_RST: return "Connection reset";
|
||||
case ERR_CLSD: return "Connection closed";
|
||||
case ERR_ARG: return "Illegal argument";
|
||||
case -55: return "DNS failed";
|
||||
default: return "UNKNOWN";
|
||||
case ERR_OK:
|
||||
return "OK";
|
||||
case ERR_MEM:
|
||||
return "Out of memory error";
|
||||
case ERR_BUF:
|
||||
return "Buffer error";
|
||||
case ERR_TIMEOUT:
|
||||
return "Timeout";
|
||||
case ERR_RTE:
|
||||
return "Routing problem";
|
||||
case ERR_INPROGRESS:
|
||||
return "Operation in progress";
|
||||
case ERR_VAL:
|
||||
return "Illegal value";
|
||||
case ERR_WOULDBLOCK:
|
||||
return "Operation would block";
|
||||
case ERR_USE:
|
||||
return "Address in use";
|
||||
case ERR_ALREADY:
|
||||
return "Already connected";
|
||||
case ERR_CONN:
|
||||
return "Not connected";
|
||||
case ERR_IF:
|
||||
return "Low-level netif error";
|
||||
case ERR_ABRT:
|
||||
return "Connection aborted";
|
||||
case ERR_RST:
|
||||
return "Connection reset";
|
||||
case ERR_CLSD:
|
||||
return "Connection closed";
|
||||
case ERR_ARG:
|
||||
return "Illegal argument";
|
||||
case -55:
|
||||
return "DNS failed";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char * AsyncClient::stateToString() {
|
||||
switch (state()) {
|
||||
case 0: return "Closed";
|
||||
case 1: return "Listen";
|
||||
case 2: return "SYN Sent";
|
||||
case 3: return "SYN Received";
|
||||
case 4: return "Established";
|
||||
case 5: return "FIN Wait 1";
|
||||
case 6: return "FIN Wait 2";
|
||||
case 7: return "Close Wait";
|
||||
case 8: return "Closing";
|
||||
case 9: return "Last ACK";
|
||||
case 10: return "Time Wait";
|
||||
default: return "UNKNOWN";
|
||||
case 0:
|
||||
return "Closed";
|
||||
case 1:
|
||||
return "Listen";
|
||||
case 2:
|
||||
return "SYN Sent";
|
||||
case 3:
|
||||
return "SYN Received";
|
||||
case 4:
|
||||
return "Established";
|
||||
case 5:
|
||||
return "FIN Wait 1";
|
||||
case 6:
|
||||
return "FIN Wait 2";
|
||||
case 7:
|
||||
return "Close Wait";
|
||||
case 8:
|
||||
return "Closing";
|
||||
case 9:
|
||||
return "Last ACK";
|
||||
case 10:
|
||||
return "Time Wait";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1236,21 +1315,35 @@ int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){
|
||||
|
||||
AsyncServer::AsyncServer(IPAddress addr, uint16_t port)
|
||||
: _port(port)
|
||||
, _bind4(true)
|
||||
, _addr(addr)
|
||||
, _noDelay(false)
|
||||
, _pcb(0)
|
||||
, _connect_cb(0)
|
||||
, _connect_cb_arg(0)
|
||||
{}
|
||||
, _connect_cb_arg(0) {
|
||||
}
|
||||
|
||||
AsyncServer::AsyncServer(uint16_t port)
|
||||
AsyncServer::AsyncServer(IPv6Address addr, uint16_t port)
|
||||
: _port(port)
|
||||
, _addr((uint32_t) IPADDR_ANY)
|
||||
, _bind6(true)
|
||||
, _addr6(addr)
|
||||
, _noDelay(false)
|
||||
, _pcb(0)
|
||||
, _connect_cb(0)
|
||||
, _connect_cb_arg(0)
|
||||
{}
|
||||
, _connect_cb_arg(0) {
|
||||
}
|
||||
|
||||
AsyncServer::AsyncServer(uint16_t port)
|
||||
: _port(port)
|
||||
, _bind4(true)
|
||||
, _bind6(true)
|
||||
, _addr((uint32_t)IPADDR_ANY)
|
||||
, _addr6()
|
||||
, _noDelay(false)
|
||||
, _pcb(0)
|
||||
, _connect_cb(0)
|
||||
, _connect_cb_arg(0) {
|
||||
}
|
||||
|
||||
AsyncServer::~AsyncServer() {
|
||||
end();
|
||||
@@ -1270,16 +1363,26 @@ void AsyncServer::begin(){
|
||||
log_e("failed to start task");
|
||||
return;
|
||||
}
|
||||
int8_t err;
|
||||
_pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
|
||||
int8_t err, bind_type;
|
||||
|
||||
if (_bind4 && _bind6) {
|
||||
bind_type = IPADDR_TYPE_ANY;
|
||||
} else if (_bind6) {
|
||||
bind_type = IPADDR_TYPE_V6;
|
||||
} else {
|
||||
bind_type = IPADDR_TYPE_V4;
|
||||
}
|
||||
|
||||
_pcb = tcp_new_ip_type(bind_type);
|
||||
if (!_pcb) {
|
||||
log_e("_pcb == NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
ip_addr_t local_addr;
|
||||
local_addr.type = IPADDR_TYPE_V4;
|
||||
local_addr.type = bind_type;
|
||||
local_addr.u_addr.ip4.addr = (uint32_t)_addr;
|
||||
memcpy(local_addr.u_addr.ip6.addr, static_cast<const uint32_t *>(_addr6), sizeof(uint32_t) * 4);
|
||||
err = _tcp_bind(_pcb, &local_addr, _port);
|
||||
|
||||
if (err != ERR_OK) {
|
||||
|
||||
@@ -23,11 +23,14 @@
|
||||
#define ASYNCTCP_H_
|
||||
|
||||
#include "IPAddress.h"
|
||||
#include "IPv6Address.h"
|
||||
#include "sdkconfig.h"
|
||||
#include <functional>
|
||||
extern "C" {
|
||||
#include "freertos/semphr.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
}
|
||||
|
||||
//If core is not defined, then we are running in Arduino or PIO
|
||||
@@ -66,6 +69,7 @@ class AsyncClient {
|
||||
return !(*this == other);
|
||||
}
|
||||
bool connect(IPAddress ip, uint16_t port);
|
||||
bool connect(IPv6Address ip, uint16_t port);
|
||||
bool connect(const char * host, uint16_t port);
|
||||
void close(bool now = false);
|
||||
void stop();
|
||||
@@ -100,14 +104,18 @@ class AsyncClient {
|
||||
bool getNoDelay();
|
||||
|
||||
uint32_t getRemoteAddress();
|
||||
ip6_addr_t getRemoteAddress6();
|
||||
uint16_t getRemotePort();
|
||||
uint32_t getLocalAddress();
|
||||
ip6_addr_t getLocalAddress6();
|
||||
uint16_t getLocalPort();
|
||||
|
||||
//compatibility
|
||||
IPAddress remoteIP();
|
||||
IPv6Address remoteIP6();
|
||||
uint16_t remotePort();
|
||||
IPAddress localIP();
|
||||
IPv6Address localIP6();
|
||||
uint16_t localPort();
|
||||
|
||||
void onConnect(AcConnectHandler cb, void * arg = 0); //on successful connect
|
||||
@@ -121,7 +129,9 @@ class AsyncClient {
|
||||
|
||||
void ackPacket(struct pbuf * pb); //ack pbuf from onPacket
|
||||
size_t ack(size_t len); //ack data that you have not acked using the method below
|
||||
void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData
|
||||
void ackLater() {
|
||||
_ack_pcb = false;
|
||||
} //will not ack the current packet. Call from onData
|
||||
|
||||
const char * errorToString(int8_t error);
|
||||
const char * stateToString();
|
||||
@@ -137,9 +147,13 @@ class AsyncClient {
|
||||
static void _s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg);
|
||||
|
||||
int8_t _recv(tcp_pcb * pcb, pbuf * pb, int8_t err);
|
||||
tcp_pcb * pcb(){ return _pcb; }
|
||||
tcp_pcb * pcb() {
|
||||
return _pcb;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool _connect(ip_addr_t addr, uint16_t port);
|
||||
|
||||
tcp_pcb * _pcb;
|
||||
int8_t _closed_slot;
|
||||
|
||||
@@ -188,6 +202,7 @@ class AsyncClient {
|
||||
class AsyncServer {
|
||||
public:
|
||||
AsyncServer(IPAddress addr, uint16_t port);
|
||||
AsyncServer(IPv6Address addr, uint16_t port);
|
||||
AsyncServer(uint16_t port);
|
||||
~AsyncServer();
|
||||
void onClient(AcConnectHandler cb, void * arg);
|
||||
@@ -203,7 +218,10 @@ class AsyncServer {
|
||||
|
||||
protected:
|
||||
uint16_t _port;
|
||||
bool _bind4 = false;
|
||||
bool _bind6 = false;
|
||||
IPAddress _addr;
|
||||
IPv6Address _addr6;
|
||||
bool _noDelay;
|
||||
tcp_pcb * _pcb;
|
||||
AcConnectHandler _connect_cb;
|
||||
|
||||
@@ -321,7 +321,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
outLen = sprintf_P((char*)buf+headLen, PSTR("%x"), readLen) + headLen;
|
||||
outLen = snprintf_P((char*)buf+headLen, sizeof(buf)-headLen-2, PSTR("%x"), readLen) + headLen;
|
||||
while(outLen < headLen + 4) buf[outLen++] = ' ';
|
||||
buf[outLen++] = '\r';
|
||||
buf[outLen++] = '\n';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Marvin Roger
|
||||
Copyright (c) 2015-2021 Marvin Roger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -6,14 +6,16 @@ AsyncMqttClient::AsyncMqttClient()
|
||||
, _tail(nullptr)
|
||||
, _sent(0)
|
||||
, _state(DISCONNECTED)
|
||||
, _tlsBadFingerprint(false)
|
||||
, _disconnectReason(AsyncMqttClientDisconnectReason::TCP_DISCONNECTED)
|
||||
, _lastClientActivity(0)
|
||||
, _lastServerActivity(0)
|
||||
, _lastPingRequestTime(0)
|
||||
, _generatedClientId{0}
|
||||
, _ip()
|
||||
, _ipv6()
|
||||
, _host(nullptr)
|
||||
, _useIp(false)
|
||||
, _useIpv6(false)
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
, _secure(false)
|
||||
#endif
|
||||
@@ -112,18 +114,35 @@ AsyncMqttClient& AsyncMqttClient::setWill(const char* topic, uint8_t qos, bool r
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setServer(IPAddress ip, uint16_t port) {
|
||||
_useIp = true;
|
||||
_useIpv6 = false;
|
||||
_ip = ip;
|
||||
_port = port;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setServer(const char* host, uint16_t port) {
|
||||
AsyncMqttClient& AsyncMqttClient::setServer(IPv6Address ipv6, uint16_t port) {
|
||||
_useIpv6 = true;
|
||||
_useIp = false;
|
||||
_host = host;
|
||||
_ipv6 = ipv6;
|
||||
_port = port;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setServer(const char* host, uint16_t port) {
|
||||
_port = port;
|
||||
_useIp = false;
|
||||
_useIpv6 = false;
|
||||
_host = host;
|
||||
if (_ipv6.fromString(host)) {
|
||||
_useIpv6 = true;
|
||||
_useIp = false;
|
||||
} else if (_ip.fromString(host)) {
|
||||
_useIpv6 = false;
|
||||
_useIp = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
AsyncMqttClient& AsyncMqttClient::setSecure(bool secure) {
|
||||
_secure = secure;
|
||||
@@ -175,11 +194,12 @@ void AsyncMqttClient::_freeCurrentParsedPacket() {
|
||||
|
||||
void AsyncMqttClient::_clear() {
|
||||
_lastPingRequestTime = 0;
|
||||
_tlsBadFingerprint = false;
|
||||
_freeCurrentParsedPacket();
|
||||
_clearQueue(true); // keep session data for now
|
||||
|
||||
_parsingInformation.bufferState = AsyncMqttClientInternals::BufferState::NONE;
|
||||
|
||||
_client.setRxTimeout(0);
|
||||
}
|
||||
|
||||
/* TCP */
|
||||
@@ -198,7 +218,7 @@ void AsyncMqttClient::_onConnect() {
|
||||
}
|
||||
|
||||
if (!sslFoundFingerprint) {
|
||||
_tlsBadFingerprint = true;
|
||||
_disconnectReason = AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT;
|
||||
_client.close(true);
|
||||
return;
|
||||
}
|
||||
@@ -222,17 +242,10 @@ void AsyncMqttClient::_onConnect() {
|
||||
void AsyncMqttClient::_onDisconnect() {
|
||||
log_i("TCP disconn");
|
||||
_state = DISCONNECTED;
|
||||
AsyncMqttClientDisconnectReason reason;
|
||||
|
||||
if (_tlsBadFingerprint) {
|
||||
reason = AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT;
|
||||
} else {
|
||||
reason = AsyncMqttClientDisconnectReason::TCP_DISCONNECTED;
|
||||
}
|
||||
|
||||
_clear();
|
||||
|
||||
for (auto callback : _onDisconnectUserCallbacks) callback(reason);
|
||||
for (auto callback : _onDisconnectUserCallbacks) callback(_disconnectReason);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -267,6 +280,7 @@ void AsyncMqttClient::_onData(char* data, size_t len) {
|
||||
case AsyncMqttClientInternals::PacketType.CONNACK:
|
||||
log_i("rcv CONNACK");
|
||||
_currentParsedPacket = new AsyncMqttClientInternals::ConnAckPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onConnAck, this, std::placeholders::_1, std::placeholders::_2));
|
||||
_client.setRxTimeout(0);
|
||||
break;
|
||||
case AsyncMqttClientInternals::PacketType.PINGRESP:
|
||||
log_i("rcv PINGRESP");
|
||||
@@ -519,6 +533,8 @@ void AsyncMqttClient::_onConnAck(bool sessionPresent, uint8_t connectReturnCode)
|
||||
for (auto callback : _onConnectUserCallbacks) callback(sessionPresent);
|
||||
} else {
|
||||
// Callbacks are handled by the onDisconnect function which is called from the AsyncTcp lib
|
||||
_disconnectReason = static_cast<AsyncMqttClientDisconnectReason>(connectReturnCode);
|
||||
return;
|
||||
}
|
||||
_handleQueue(); // send any remaining data from continued session
|
||||
}
|
||||
@@ -688,6 +704,9 @@ void AsyncMqttClient::connect() {
|
||||
if (_state != DISCONNECTED) return;
|
||||
log_i("CONNECTING");
|
||||
_state = CONNECTING;
|
||||
_disconnectReason = AsyncMqttClientDisconnectReason::TCP_DISCONNECTED; // reset any previous
|
||||
|
||||
_client.setRxTimeout(_keepAlive);
|
||||
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
if (_useIp) {
|
||||
@@ -698,6 +717,8 @@ void AsyncMqttClient::connect() {
|
||||
#else
|
||||
if (_useIp) {
|
||||
_client.connect(_ip, _port);
|
||||
} else if (_useIpv6) {
|
||||
_client.connect(_ipv6, _port);
|
||||
} else {
|
||||
_client.connect(_host, _port);
|
||||
}
|
||||
@@ -744,6 +765,12 @@ uint16_t AsyncMqttClient::publish(const char* topic, uint8_t qos, bool retain, c
|
||||
return msg->packetId();
|
||||
}
|
||||
|
||||
bool AsyncMqttClient::clearQueue() {
|
||||
if (_state != DISCONNECTED) return false;
|
||||
_clearQueue(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* AsyncMqttClient::getClientId() const {
|
||||
return _clientId;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ class AsyncMqttClient {
|
||||
AsyncMqttClient& setCredentials(const char* username, const char* password = nullptr);
|
||||
AsyncMqttClient& setWill(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0);
|
||||
AsyncMqttClient& setServer(IPAddress ip, uint16_t port);
|
||||
AsyncMqttClient& setServer(IPv6Address ipv6, uint16_t port);
|
||||
AsyncMqttClient& setServer(const char* host, uint16_t port);
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
AsyncMqttClient& setSecure(bool secure);
|
||||
@@ -81,6 +82,7 @@ class AsyncMqttClient {
|
||||
uint16_t subscribe(const char* topic, uint8_t qos);
|
||||
uint16_t unsubscribe(const char* topic);
|
||||
uint16_t publish(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0, bool dup = false, uint16_t message_id = 0);
|
||||
bool clearQueue(); // Not MQTT compliant!
|
||||
|
||||
const char* getClientId() const;
|
||||
|
||||
@@ -95,15 +97,17 @@ class AsyncMqttClient {
|
||||
DISCONNECTING,
|
||||
DISCONNECTED
|
||||
} _state;
|
||||
bool _tlsBadFingerprint;
|
||||
AsyncMqttClientDisconnectReason _disconnectReason;
|
||||
uint32_t _lastClientActivity;
|
||||
uint32_t _lastServerActivity;
|
||||
uint32_t _lastPingRequestTime;
|
||||
|
||||
char _generatedClientId[18 + 1]; // esp8266-abc123 and esp32-abcdef123456
|
||||
IPAddress _ip;
|
||||
IPv6Address _ipv6;
|
||||
const char* _host;
|
||||
bool _useIp;
|
||||
bool _useIpv6;
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
bool _secure;
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
enum class AsyncMqttClientDisconnectReason : int8_t {
|
||||
enum class AsyncMqttClientDisconnectReason : uint8_t {
|
||||
TCP_DISCONNECTED = 0,
|
||||
|
||||
MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 1,
|
||||
|
||||
@@ -10,7 +10,6 @@ PubAckOutPacket::PubAckOutPacket(PendingAck pendingAck) {
|
||||
_packetId = pendingAck.packetId;
|
||||
_data[2] = pendingAck.packetId >> 8;
|
||||
_data[3] = pendingAck.packetId & 0xFF;
|
||||
// _released = false;
|
||||
if (packetType() == AsyncMqttClientInternals::PacketType.PUBREL ||
|
||||
packetType() == AsyncMqttClientInternals::PacketType.PUBREC) {
|
||||
_released = false;
|
||||
|
||||
@@ -47,6 +47,7 @@ void APSettingsService::manageAP() {
|
||||
|
||||
void APSettingsService::startAP() {
|
||||
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
|
||||
esp_wifi_set_bandwidth(ESP_IF_WIFI_AP, WIFI_BW_HT20);
|
||||
WiFi.softAP(_state.ssid.c_str(), _state.password.c_str());
|
||||
if (!_dnsServer) {
|
||||
IPAddress apIp = WiFi.softAPIP();
|
||||
|
||||
@@ -84,7 +84,7 @@ void MqttSettingsService::onMqttConnect(bool sessionPresent) {
|
||||
}
|
||||
|
||||
void MqttSettingsService::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
|
||||
// emsesp::EMSESP::logger().info(F("Disconnected from MQTT reason: %s"), (uint8_t)reason);
|
||||
// emsesp::EMSESP::logger().info(F("Disconnected from MQTT reason: %d"), (uint8_t)reason);
|
||||
_disconnectReason = reason;
|
||||
_disconnectedAt = uuid::get_uptime();
|
||||
}
|
||||
@@ -101,6 +101,7 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
switch (event) {
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
case SYSTEM_EVENT_ETH_GOT_IP:
|
||||
case SYSTEM_EVENT_GOT_IP6:
|
||||
if (_state.enabled) {
|
||||
// emsesp::EMSESP::logger().info(F("Network connection found, starting MQTT client"));
|
||||
onConfigUpdated();
|
||||
@@ -121,14 +122,13 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
}
|
||||
|
||||
void MqttSettingsService::configureMqtt() {
|
||||
// disconnect if currently connected
|
||||
_mqttClient.disconnect();
|
||||
|
||||
// only connect if WiFi is connected and MQTT is enabled
|
||||
if (_state.enabled && emsesp::EMSESP::system_.network_connected()) {
|
||||
_mqttClient.disconnect();
|
||||
_mqttClient.setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
|
||||
if (_state.username.length() > 0) {
|
||||
_mqttClient.setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername), retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword));
|
||||
_mqttClient.setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername),
|
||||
retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword));
|
||||
} else {
|
||||
_mqttClient.setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword));
|
||||
}
|
||||
@@ -138,8 +138,6 @@ void MqttSettingsService::configureMqtt() {
|
||||
_mqttClient.setMaxTopicLength(_state.maxTopicLength);
|
||||
_mqttClient.connect();
|
||||
}
|
||||
|
||||
emsesp::EMSESP::dallassensor_.reload(); // added by Proddy for EMS-ESP
|
||||
}
|
||||
|
||||
void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
|
||||
@@ -163,8 +161,6 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
|
||||
root["publish_time_sensor"] = settings.publish_time_sensor;
|
||||
root["mqtt_qos"] = settings.mqtt_qos;
|
||||
root["mqtt_retain"] = settings.mqtt_retain;
|
||||
root["dallas_format"] = settings.dallas_format;
|
||||
root["bool_format"] = settings.bool_format;
|
||||
root["ha_climate_format"] = settings.ha_climate_format;
|
||||
root["ha_enabled"] = settings.ha_enabled;
|
||||
root["nested_format"] = settings.nested_format;
|
||||
@@ -195,20 +191,17 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
|
||||
newSettings.publish_time_other = root["publish_time_other"] | EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
newSettings.publish_time_sensor = root["publish_time_sensor"] | EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
|
||||
newSettings.dallas_format = root["dallas_format"] | EMSESP_DEFAULT_DALLAS_FORMAT;
|
||||
newSettings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT;
|
||||
newSettings.ha_climate_format = root["ha_climate_format"] | EMSESP_DEFAULT_HA_CLIMATE_FORMAT;
|
||||
newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED;
|
||||
newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT;
|
||||
newSettings.subscribe_format = root["subscribe_format"] | EMSESP_DEFAULT_SUBSCRIBE_FORMAT;
|
||||
|
||||
if (newSettings.mqtt_qos != settings.mqtt_qos) {
|
||||
emsesp::EMSESP::mqtt_.set_qos(newSettings.mqtt_qos);
|
||||
if (newSettings.enabled != settings.enabled) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (newSettings.dallas_format != settings.dallas_format) {
|
||||
emsesp::EMSESP::mqtt_.dallas_format(newSettings.dallas_format);
|
||||
if (newSettings.mqtt_qos != settings.mqtt_qos) {
|
||||
emsesp::EMSESP::mqtt_.set_qos(newSettings.mqtt_qos);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include <uuid/common.h>
|
||||
|
||||
#define MQTT_RECONNECTION_DELAY 1000
|
||||
#define MQTT_RECONNECTION_DELAY 2000 // 2 seconds
|
||||
|
||||
#define MQTT_SETTINGS_FILE "/config/mqttSettings.json"
|
||||
#define MQTT_SETTINGS_SERVICE_PATH "/rest/mqttSettings"
|
||||
@@ -86,8 +86,6 @@ class MqttSettings {
|
||||
uint16_t publish_time_sensor;
|
||||
uint8_t mqtt_qos;
|
||||
bool mqtt_retain;
|
||||
uint8_t dallas_format;
|
||||
uint8_t bool_format;
|
||||
uint8_t ha_climate_format;
|
||||
bool ha_enabled;
|
||||
uint8_t nested_format;
|
||||
|
||||
@@ -62,6 +62,19 @@ void NetworkSettingsService::manageSTA() {
|
||||
}
|
||||
|
||||
WiFi.setHostname(_state.hostname.c_str()); // set hostname
|
||||
// www.esp32.com/viewtopic.php?t=12055
|
||||
read([&](NetworkSettings & networkSettings) {
|
||||
if (networkSettings.bandwidth20) {
|
||||
esp_wifi_set_bandwidth(ESP_IF_WIFI_STA, WIFI_BW_HT20);
|
||||
} else {
|
||||
esp_wifi_set_bandwidth(ESP_IF_WIFI_STA, WIFI_BW_HT40);
|
||||
}
|
||||
esp_wifi_set_max_tx_power(networkSettings.tx_power * 4);
|
||||
if (networkSettings.nosleep) {
|
||||
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
||||
}
|
||||
|
||||
});
|
||||
WiFi.begin(_state.ssid.c_str(), _state.password.c_str()); // attempt to connect to the network
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
#include <HttpEndpoint.h>
|
||||
#include <JsonUtils.h>
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include <esp_wifi.h>
|
||||
#include <ETH.h>
|
||||
#endif
|
||||
|
||||
#define NETWORK_SETTINGS_FILE "/config/networkSettings.json"
|
||||
#define NETWORK_SETTINGS_SERVICE_PATH "/rest/networkSettings"
|
||||
@@ -31,6 +34,10 @@ class NetworkSettings {
|
||||
String password;
|
||||
String hostname;
|
||||
bool staticIPConfig;
|
||||
bool enableIPv6;
|
||||
bool bandwidth20;
|
||||
int8_t tx_power;
|
||||
bool nosleep;
|
||||
|
||||
// optional configuration for static IP address
|
||||
IPAddress localIP;
|
||||
@@ -45,6 +52,10 @@ class NetworkSettings {
|
||||
root["password"] = settings.password;
|
||||
root["hostname"] = settings.hostname;
|
||||
root["static_ip_config"] = settings.staticIPConfig;
|
||||
root["enableIPv6"] = settings.enableIPv6;
|
||||
root["bandwidth20"] = settings.bandwidth20;
|
||||
root["tx_power"] = settings.tx_power;
|
||||
root["nosleep"] = settings.nosleep;
|
||||
|
||||
// extended settings
|
||||
JsonUtils::writeIP(root, "local_ip", settings.localIP);
|
||||
@@ -59,6 +70,10 @@ class NetworkSettings {
|
||||
settings.password = root["password"] | FACTORY_WIFI_PASSWORD;
|
||||
settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME;
|
||||
settings.staticIPConfig = root["static_ip_config"] | false;
|
||||
settings.enableIPv6 = root["enableIPv6"] | false;
|
||||
settings.bandwidth20 = root["bandwidth20"] | false;
|
||||
settings.tx_power = root["tx_power"] | 20;
|
||||
settings.nosleep = root["nosleep"] | false;
|
||||
|
||||
// extended settings
|
||||
JsonUtils::readIP(root, "local_ip", settings.localIP);
|
||||
|
||||
@@ -25,6 +25,7 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
// for Wifi
|
||||
if (wifi_status == WL_CONNECTED) {
|
||||
root["local_ip"] = WiFi.localIP().toString();
|
||||
root["local_ipv6"] = WiFi.localIPv6().toString();
|
||||
root["mac_address"] = WiFi.macAddress();
|
||||
root["rssi"] = WiFi.RSSI();
|
||||
root["ssid"] = WiFi.SSID();
|
||||
@@ -47,6 +48,7 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
} else if (ethernet_connected) {
|
||||
// Ethernet
|
||||
root["local_ip"] = ETH.localIP().toString();
|
||||
root["local_ipv6"] = ETH.localIPv6().toString();
|
||||
root["mac_address"] = ETH.macAddress();
|
||||
root["subnet_mask"] = ETH.subnetMask().toString();
|
||||
root["gateway_ip"] = ETH.gatewayIP().toString();
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
#ifndef DEFAULT_BUFFER_SIZE
|
||||
#define DEFAULT_BUFFER_SIZE 1024
|
||||
#define DEFAULT_BUFFER_SIZE 2048
|
||||
#endif
|
||||
|
||||
enum class StateUpdateResult {
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
|
||||
#include "uuid/syslog.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#include "../../../src/emsesp.h"
|
||||
|
||||
@@ -153,16 +150,38 @@ void SyslogService::maximum_log_messages(size_t count) {
|
||||
}
|
||||
|
||||
std::pair<IPAddress, uint16_t> SyslogService::destination() const {
|
||||
return std::make_pair(host_, port_);
|
||||
return std::make_pair(ip_, port_);
|
||||
}
|
||||
|
||||
void SyslogService::destination(IPAddress host, uint16_t port) {
|
||||
host_ = host;
|
||||
ip_ = host;
|
||||
port_ = port;
|
||||
|
||||
if ((uint32_t)host_ == (uint32_t)0) {
|
||||
if ((uint32_t)ip_ == (uint32_t)0) {
|
||||
started_ = false;
|
||||
remove_queued_messages(log_level());
|
||||
host_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void SyslogService::destination(const char * host, uint16_t port) {
|
||||
if (host == nullptr || host[0] == '\0') {
|
||||
started_ = false;
|
||||
remove_queued_messages(log_level());
|
||||
ip_ = (IPAddress)(uint32_t)0;
|
||||
host_.clear();
|
||||
return;
|
||||
}
|
||||
host_ = host;
|
||||
port_ = port;
|
||||
if (ip_.fromString(host)) {
|
||||
host_.clear();
|
||||
if ((uint32_t)ip_ == (uint32_t)0) {
|
||||
started_ = false;
|
||||
remove_queued_messages(log_level());
|
||||
}
|
||||
} else {
|
||||
ip_ = (IPAddress)(uint32_t)0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,12 +276,15 @@ void SyslogService::loop() {
|
||||
}
|
||||
|
||||
bool SyslogService::can_transmit() {
|
||||
if (!host_.empty() && (uint32_t)ip_ == 0) {
|
||||
WiFi.hostByName(host_.c_str(), ip_);
|
||||
}
|
||||
#if UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||
if (host_.isV4() && (uint32_t)host_ == (uint32_t)0) {
|
||||
if (ip_.isV4() && (uint32_t)ip_ == (uint32_t)0) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if ((uint32_t)host_ == (uint32_t)0) {
|
||||
if ((uint32_t)ip_ == (uint32_t)0) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -276,14 +298,14 @@ bool SyslogService::can_transmit() {
|
||||
|
||||
#if UUID_SYSLOG_ARP_CHECK
|
||||
#if UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||
if (host_.isV4())
|
||||
if (ip_.isV4())
|
||||
#endif
|
||||
{
|
||||
message_delay = 10;
|
||||
}
|
||||
#endif
|
||||
#if UUID_SYSLOG_NDP_CHECK && UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||
if (host_.isV6()) {
|
||||
if (ip_.isV6()) {
|
||||
message_delay = 10;
|
||||
}
|
||||
#endif
|
||||
@@ -294,12 +316,12 @@ bool SyslogService::can_transmit() {
|
||||
|
||||
#if UUID_SYSLOG_ARP_CHECK
|
||||
#if UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||
if (host_.isV4())
|
||||
if (ip_.isV4())
|
||||
#endif
|
||||
{
|
||||
ip4_addr_t ipaddr;
|
||||
|
||||
ip4_addr_set_u32(&ipaddr, (uint32_t)host_);
|
||||
ip4_addr_set_u32(&ipaddr, (uint32_t)ip_);
|
||||
|
||||
if (!ip4_addr_isloopback(&ipaddr) && !ip4_addr_ismulticast(&ipaddr) && !ip4_addr_isbroadcast(&ipaddr, netif_default)) {
|
||||
struct eth_addr * eth_ret = nullptr;
|
||||
@@ -326,10 +348,10 @@ bool SyslogService::can_transmit() {
|
||||
#endif
|
||||
|
||||
#if UUID_SYSLOG_NDP_CHECK && UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||
if (host_.isV6()) {
|
||||
if (ip_.isV6()) {
|
||||
ip6_addr_t ip6addr;
|
||||
|
||||
IP6_ADDR(&ip6addr, host_.raw6()[0], host_.raw6()[1], host_.raw6()[2], host_.raw6()[3]);
|
||||
IP6_ADDR(&ip6addr, ip_.raw6()[0], ip_.raw6()[1], ip_.raw6()[2], ip_.raw6()[3]);
|
||||
ip6_addr_assign_zone(&ip6addr, IP6_UNICAST, netif_default);
|
||||
|
||||
if (!ip6_addr_isloopback(&ip6addr) && !ip6_addr_ismulticast(&ip6addr)) {
|
||||
@@ -400,7 +422,7 @@ bool SyslogService::transmit(const QueuedLogMessage & message) {
|
||||
tzm = diff < 0 ? (0 - diff) % 60 : diff % 60;
|
||||
}
|
||||
|
||||
if (udp_.beginPacket(host_, port_) != 1) {
|
||||
if (udp_.beginPacket(ip_, port_) != 1) {
|
||||
last_transmit_ = uuid::get_uptime_ms();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -129,6 +129,7 @@ class SyslogService : public uuid::log::Handler {
|
||||
* @since 2.0.0
|
||||
*/
|
||||
void destination(IPAddress host, uint16_t port = DEFAULT_PORT);
|
||||
void destination(const char * host, uint16_t port = DEFAULT_PORT);
|
||||
|
||||
/**
|
||||
* Get local hostname.
|
||||
@@ -183,6 +184,20 @@ class SyslogService : public uuid::log::Handler {
|
||||
*/
|
||||
virtual void operator<<(std::shared_ptr<uuid::log::Message> message);
|
||||
|
||||
/**
|
||||
* added MichaelDvP
|
||||
* query status variables
|
||||
*/
|
||||
size_t queued() {
|
||||
return log_messages_.size();
|
||||
}
|
||||
bool started() {
|
||||
return started_;
|
||||
}
|
||||
IPAddress ip() {
|
||||
return ip_;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Log message that has been queued.
|
||||
@@ -244,7 +259,8 @@ class SyslogService : public uuid::log::Handler {
|
||||
|
||||
bool started_ = false; /*!< Flag to indicate that messages have started being transmitted. @since 1.0.0 */
|
||||
WiFiUDP udp_; /*!< UDP client. @since 1.0.0 */
|
||||
IPAddress host_; /*!< Host to send messages to. @since 1.0.0 */
|
||||
IPAddress ip_; /*!< Host-IP to send messages to. @since 1.0.0 */
|
||||
std::string host_; /*!< Host to send messages to. */
|
||||
uint16_t port_ = DEFAULT_PORT; /*!< Port to send messages to. @since 1.0.0 */
|
||||
uint64_t last_transmit_ = 0; /*!< Last transmit time. @since 1.0.0 */
|
||||
std::string hostname_{'-'}; /*!< Local hostname. @since 1.0.0 */
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <string>
|
||||
#include <algorithm> // for count_if
|
||||
|
||||
#include "WString.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define IPAddress std::string
|
||||
@@ -129,7 +131,12 @@ class Print {
|
||||
size_t println(unsigned long value) {
|
||||
return print(std::to_string(value).c_str()) + println();
|
||||
}
|
||||
|
||||
virtual void flush(){};
|
||||
|
||||
size_t print(const String & str) {
|
||||
return print(str.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class Stream : public Print {
|
||||
@@ -206,6 +213,6 @@ void yield(void);
|
||||
void setup(void);
|
||||
void loop(void);
|
||||
|
||||
#include "WString.h"
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,13 +26,15 @@ class DummySettings {
|
||||
bool shower_alert = false;
|
||||
bool hide_led = false;
|
||||
bool notoken_api = false;
|
||||
uint8_t bool_format = 1; // on off
|
||||
uint8_t enum_format = 1;
|
||||
uint8_t dallas_format = 1;
|
||||
|
||||
// MQTT
|
||||
uint16_t publish_time = 10; // seconds
|
||||
uint8_t mqtt_qos = 0;
|
||||
bool mqtt_retain = false;
|
||||
bool enabled = true;
|
||||
uint8_t dallas_format = 1;
|
||||
uint8_t nested_format = 1;
|
||||
uint8_t ha_climate_format = 1;
|
||||
bool ha_enabled = true;
|
||||
@@ -56,7 +58,7 @@ class DummySettings {
|
||||
uint16_t publish_time_mixer = 10;
|
||||
uint16_t publish_time_other = 10;
|
||||
uint16_t publish_time_sensor = 10;
|
||||
uint8_t bool_format = 1; // on off
|
||||
bool enableIPv6 = false;
|
||||
|
||||
#define FACTORY_MQTT_MAX_TOPIC_LENGTH 128
|
||||
|
||||
@@ -79,6 +81,7 @@ class DummySettingsService : public StatefulService<DummySettings> {
|
||||
#define NetworkSettings DummySettings
|
||||
#define SecuritySettings DummySettings
|
||||
#define MqttSettings DummySettings
|
||||
#define NTPSettings DummySettings
|
||||
|
||||
class ESP8266React {
|
||||
public:
|
||||
@@ -109,6 +112,10 @@ class ESP8266React {
|
||||
return &_settings;
|
||||
}
|
||||
|
||||
StatefulService<DummySettings> * getNTPSettingsService() {
|
||||
return &_settings;
|
||||
}
|
||||
|
||||
private:
|
||||
DummySettingsService _settings;
|
||||
SecuritySettingsService _securitySettingsService;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <functional>
|
||||
|
||||
#ifndef DEFAULT_BUFFER_SIZE
|
||||
#define DEFAULT_BUFFER_SIZE 1024
|
||||
#define DEFAULT_BUFFER_SIZE 2048
|
||||
#endif
|
||||
|
||||
enum class StateUpdateResult {
|
||||
|
||||
@@ -45,6 +45,13 @@ class String {
|
||||
return _str == s;
|
||||
}
|
||||
|
||||
bool concat(const char * rhs) {
|
||||
if (!rhs) {
|
||||
return 0;
|
||||
}
|
||||
_str += rhs;
|
||||
return 1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _str;
|
||||
@@ -56,6 +63,10 @@ inline bool operator==(const std::string & lhs, const ::String & rhs) {
|
||||
return lhs == rhs.c_str();
|
||||
}
|
||||
|
||||
inline bool operator!=(const ::String & lhs, const ::String & rhs) {
|
||||
return lhs.c_str() != rhs.c_str();
|
||||
}
|
||||
|
||||
size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize);
|
||||
size_t strlcat(char * dst, const char * src, size_t siz);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user