Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into feature/SPI-add-support-for-multiple-buses
@@ -24,7 +24,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Build documentation
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- id: set-matrix
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Dependencies
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Get current date
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- id: set-matrix
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
- name: Dependencies
|
||||
run: |
|
||||
sudo apt install binutils build-essential libffi-dev libgit2-dev
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
- name: Build documentation
|
||||
run: |
|
||||
cd docs
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
notes: ${{ steps.release-notes.outputs.result }}
|
||||
steps:
|
||||
- id: release-notes
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
needs: [build, prepare-dist, prepare-notes]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Get current date
|
||||
|
||||
@@ -85,6 +85,8 @@ A group of MQTT specific settings:
|
||||
|
||||
* **Publish Retain Flag**: When publishing data accompanied by the Retain flag, the server will store the last sent data, and publish that when reconnecting.
|
||||
|
||||
This will override the **Retained** setting per Task that can be set for a MQTT Controller.
|
||||
|
||||
* **Controller Subscribe**: The topic(s) to subscribe to, so the MQTT Server can send data and optionally commands to this client-instance.
|
||||
|
||||
* **Controller Publish**: The topic to publish data to. This field supports variables so each value sent to the server can have a separate topic for its data.
|
||||
|
||||
@@ -22,7 +22,7 @@ Depending on the controller capabilities, some configuration settings may be sho
|
||||
|
||||
.. image:: Task_config_page_Controllers_section.png
|
||||
|
||||
All configured Controllers are shown here, including their enabled or disabled state (only a single MQTT Controller can be enabled at one time).
|
||||
All configured Controllers are shown here, including the enabled or disabled state (multiple Controllers can be enabled, only a single MQTT Controller can be enabled at one time!).
|
||||
|
||||
For each controller the user can select wether the data should be sent on each Interval (or explicit TaskRun).
|
||||
|
||||
@@ -30,12 +30,14 @@ For the Domoticz controllers the value index (IDX) has to be configured.
|
||||
|
||||
For some controllers, like Home Assistant/openHAB, there are extra options available.
|
||||
|
||||
* **Group**: This represents the group id to combine all values from multiple tasks into a single grouped-device during MQTT AutoDiscovery. Groups, by design, can span multiple ESPEasy devices, if desired, as long as the Task/Valuename combinations are unique. If a group should only combine Tasks from a single ESPEasy unit, the group id should be unique across multiple ESPEasy units. The group description, default *Group <n>*, can be adjusted in Home Assistant.
|
||||
* **Group**: This represents the group id to combine all values from multiple tasks into a single grouped-device during MQTT AutoDiscovery. Groups, by design, can span multiple ESPEasy devices, if desired, as long as the Task/Valuename combinations are unique. If a group should only combine Tasks from a single ESPEasy unit, the group id should be unique across multiple ESPEasy units. The group description, default *Group <n>*, can be adjusted in Home Assistant. If the Group value matches the current Unit nr, the Unit name, ``%sysname%``, is used instead of Group <nr>.
|
||||
|
||||
* **Retained**: For MQTT Controllers, this setting can be enabled to send the values for the current task with the Retain flag set. The **Publish Retain flag** in the Controller settings will override this by sending *all* task values with Retain flag enabled.
|
||||
|
||||
* **Send derived**: This checkbox determines if any configured Derived values should also be sent to the controller (and included in the AutoDiscovery if that's available and enabled).
|
||||
|
||||
* **Resend MQTT Discovery**: When checked, will start a resend of the MQTT Discovery process for this task after a random delay, when Submit is clicked, so any chenged settings can be updated in the MQTT server. This setting is only available if the controller is enabled, the Auto Discovery feature is available and enabled for the controller. This setting is not stored.
|
||||
* **Resend MQTT Discovery**: When checked, will start a resend of the MQTT Discovery process for this task after a random delay, when Submit is clicked, so any changed settings will be updated in the MQTT broker. This setting is only available if the controller is enabled, the Auto Discovery feature is available and enabled for the controller. This setting is not stored.
|
||||
|
||||
Other controllers, like FHEM HTTP, do not support additional settings besides the checkbox to enable sending the data.
|
||||
Other controllers, like f.e. FHEM HTTP, do not support additional settings besides the checkbox to enable sending the data.
|
||||
|
||||
|
|
||||
|
||||
@@ -179,7 +179,7 @@ The number of mappings is currently limited to 25, but only the number of used m
|
||||
Option: Generate events for accepted topics
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* **Generate events for accepted topics**: Enabling this option will generate an event for every incoming, and accepted if Filtering is used, non-JSON payload even when that has a non-numeric value. The event generated is: ``<devicename>#<valuename>=<value>``, for example ``MQTT_Import#Value1=on``.
|
||||
* **Generate events for accepted topics**: Enabling this option will generate an event for every incoming, and accepted if Filtering is used, non-JSON payload even when that has a non-numeric value. The event generated is: ``<devicename>#<valuename>=<value>``, for example ``MQTT_Import#Value1=on``. When the ``<value>`` is numeric, the configured number of decimals is applied to the value.
|
||||
|
||||
When this option is disabled it has the backward-compatible behavior of discarding that message as invalid.
|
||||
|
||||
|
||||
@@ -23,28 +23,52 @@ Maintainer: |P056_maintainer|
|
||||
|
||||
Used libraries: |P056_usedlibraries|
|
||||
|
||||
Supported hardware
|
||||
------------------
|
||||
Introduction
|
||||
------------
|
||||
|
||||
|P056_usedby|
|
||||
A dust sensor will measure the dust concentration of the surrounding air. Depending on the device, different particle sizes can be measured, and the SDS series provides values for up to 2.5 and up to 10 micrometer particle size.
|
||||
|
||||
Multiple sensors can be used independently on a single ESP unit, as long as the serial port setup is uniquely configured.
|
||||
|
||||
|
||||
Device
|
||||
------
|
||||
|
||||
.. image:: P056_DeviceConfiguration.png
|
||||
|
||||
|
||||
Task settings
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* **Name** The name of the task. This should be unique for all devices that are configured. (Initially empty)
|
||||
|
||||
* **Enabled** For the device to work it has to be enabled. When checked, the device will be started as soon as the ESP starts. If desired, the device can also be enabled from f.e. a rule by using the ``TaskEnable,<tasknr>`` or ``TaskEnable,<taskname>`` command, or disabled using the corresponding ``TaskDisable,<tasknr>|<taskname>`` commands.
|
||||
|
||||
Sensor
|
||||
^^^^^^
|
||||
~~~~~~
|
||||
|
||||
See: :ref:`SerialHelper_page`
|
||||
|
||||
Device Settings
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
**TODO**: Complete this documentation...
|
||||
* **Sleep time**: Numeric value to set the sleep time between measurements. The laser used in the sensor has an aproximated life time of 8000 hours of on-time. When enabled continuously, this number will be reached in about 333 days, so less than a year! To prolong the lifetime, a sleep interval can be configured, during which the laser will be turned on, and then turned off for ``<sleeptime>*60-30`` seconds. Range = 0 (continuous) or 1 .. 30 seconds.
|
||||
|
||||
.. Commands available
|
||||
.. ^^^^^^^^^^^^^^^^^^
|
||||
This setting is only available if the optional **ESP TX GPIO** pin is configured correctly, as it involves sending a command to the sensor.
|
||||
|
||||
.. .. include:: P056_commands.repl
|
||||
|
||||
.. Events
|
||||
.. ~~~~~~
|
||||
.. include:: DataAcquisition.repl
|
||||
|
||||
.. .. include:: P056_events.repl
|
||||
* **Interval**: By default, Interval will be set to 60 sec for this plugin. It is the frequency used to read sensor values and send these to any Controllers configured for this device. Values will *only* be sent if data is successfully received from the sensor.
|
||||
|
||||
Values
|
||||
~~~~~~
|
||||
|
||||
The measured values are available in ``PM2.5`` and ``PM10``.
|
||||
|
||||
For a complete description of all available columns see the `Plugin - Values <_Plugin.html#values>`_ section.
|
||||
|
||||
|
|
||||
|
||||
Change log
|
||||
----------
|
||||
@@ -52,6 +76,9 @@ Change log
|
||||
.. versionchanged:: 2.0
|
||||
...
|
||||
|
||||
|changed|
|
||||
2025-08-05: Enable multiple instances of the plugin, and update the documentation.
|
||||
|
||||
|added|
|
||||
Major overhaul for 2.0 release.
|
||||
|
||||
@@ -61,7 +88,3 @@ Change log
|
||||
|added|
|
||||
Initial release version.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 53 KiB |
@@ -40,6 +40,16 @@ Device Settings
|
||||
|
||||
* **Hostname**: The hostname or IP-address for the device or host to monitor.
|
||||
|
||||
**ESP32 only:**
|
||||
|
||||
* **Ping count**: The number of pings that should be tried before reporting a failure. (Only available on ESP32 builds)
|
||||
|
||||
* **Available Values**: The default is to only have a **Fails** value available, but for ESP32 there is also the Average ping-time in msec. available.
|
||||
|
||||
.. image:: P089_AvailableValuesOptions.png
|
||||
|
||||
When changing this setting, the page will save and reload to adjust for the change in **Values**, below.
|
||||
|
||||
.. include:: DataAcquisition.repl
|
||||
|
||||
* **Interval** By default, Interval will be set to 60 sec. Every Interval setting, the host will be pinged and the state will be updated in Fails. If the ping responds as intended, then Fails is reset to 0.
|
||||
@@ -47,7 +57,9 @@ Device Settings
|
||||
Values
|
||||
^^^^^^
|
||||
|
||||
The failure count value is available in ``Fails``. No other options are available for Values.
|
||||
The failure count value is available in ``Fails``. On ESP32 there is an option to also show the average ping-time in msec. via ``Avg_ms``.
|
||||
|
||||
On ESP32, per Value is a **Stats** checkbox available, that when checked, gathers the data and presents recent data in a graph, as described here: :ref:`Task Value Statistics: <Task Value Statistics>`
|
||||
|
||||
|
||||
|
||||
@@ -64,6 +76,8 @@ Change log
|
||||
.. versionchanged:: 2.0
|
||||
...
|
||||
|
||||
|added| 2025-08-26 Enable support for ESP32
|
||||
|
||||
|changed| 2023-03-14 Extended command handling to not require the taskname argument.
|
||||
|
||||
|added| 2020-02-22
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 41 KiB |
@@ -53,15 +53,9 @@ Device Settings
|
||||
.. image:: P168_LuxReadMethodOptions.png
|
||||
:alt: Lux read-method options
|
||||
|
||||
* *Normal*: Normal read mode, will report the uncorrected ``Lux`` value.
|
||||
* *Normal (no wait)*: Normal read mode, will report the uncorrected ``Lux`` value, doesn't wait for the sensor to complete a measurement, but retrieves it later after starting the read.
|
||||
|
||||
* *Corrected*: Corrected read mode, Raw calculated to ``Lux`` and corrected for non-linearity.
|
||||
|
||||
* *Auto*: Show the corrected value, auto-scaled with Gain factor and Integration time to avoid measuring errors because of over-exposing the sensor. This is the default setting.
|
||||
|
||||
* *Normal (no wait)*: Normal read mode, will report the uncorrected ``Lux`` value, doesn't wait for the sensor to complete a measurement.
|
||||
|
||||
* *Corrected (no wait)*: Corrected read mode, Raw calculated to ``Lux`` and corrected for non-linearity, doesn't wait for the sensor to complete a measurement.
|
||||
* *Corrected (no wait)*: Corrected read mode, Raw calculated to ``Lux`` and corrected for non-linearity, doesn't wait for the sensor to complete a measurement, but retrieves it later after starting the read.
|
||||
|
||||
|
|
||||
|
||||
@@ -75,8 +69,6 @@ Device Settings
|
||||
|
||||
.. image:: P168_IntegrationTimeOptions.png
|
||||
|
||||
.. warning:: When the **Lux Read-method** is set to *Auto* the Gain factor and Integration time settings are ignored.
|
||||
|
||||
* **Power Save Mode**: The sensor can go into Power Save Mode on several levels, to conserve power when used in a battery operated device. When in power save mode, the integration time increases.
|
||||
|
||||
.. image:: P168_PowerSaveModeOptions.png
|
||||
|
||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
@@ -247,13 +247,13 @@ On selected builds (ESP32 only, can be enabled in ESP8266 Custom builds) per val
|
||||
|
||||
If set the UoM will be space-appended to the value when displayed on the Devices page, used in the labels for the Stats display, and later used in the MQTT AutoDiscovery messages so the receiving server can use that for presentation. When set it is also included in the JSON output as ``UoM`` per taskvalue, available at the ``/json`` endpoint of the ESP, to be used by external systems like EasyFetch.
|
||||
|
||||
A list of 150+ Unit of Measure values is available (derived of what's supported by Home Assistant):
|
||||
A list of 165+ Unit of Measure values is available (derived of what's supported/expected by Home Assistant):
|
||||
|
||||
``°C, °F, K, %, Pa, hPa, bar, mbar, inHg, psi, W, kW, V, Wh, kWh, A, VA, mm, cm, m, km,`` ``L, mL, m³, ft³, m³/h, ft³/h, lx, UV index, µg/m³, mg/m³, p/m³, ppm, ppb,``
|
||||
``°, €, $, ¢, µs, ms, s, min, h, d, w, m, y, in, ft, yd, mi, Hz, GHz, gal, fl. oz, m²,`` ``g, kg, mg, µg, oz, lb, µS/cm, W/m², mm/h, mm/s, in/s, m/s, in/h, km/h, mph, db, dBm,``
|
||||
``bit, kbit, Mbit, Gbit, B, kB, MB, GB, TB, PB, EB, ZB, YB, KiB, MiB, GiB, TiB, PiB, EiB, ZiB, YiB,`` ``bit/s, kbit/s, Mbit/s, Gbit/s, B/s, kB/s, MB/s, GB/s, KiB/s, MiB/s, GiB/s,``
|
||||
``ft/s, kn, mW, MW, GW, TW, BTU/(h·ft²), pH, cbar, mmHg, kPa, mA, µA, mV, µV, kV, cm², km², mm²,`` ``in², ft², yd², mi², ac, ha, kHz, MHz, mWh, MWh, GWh, TWh, cal, kcal, Mcal,``
|
||||
``Gcal, J, kJ, MJ, GJ, var, kvar, varh, kvarh, st, mg/dL, mmol/L, µSv, µSv/h, m³/s, ft³/min,`` ``L/h, L/min, L/s, gal/min, mL/s``
|
||||
``Gcal, J, kJ, MJ, GJ, var, kvar, varh, kvarh, st, mg/dL, mmol/L, µSv, µSv/h, m³/s, ft³/min,`` ``L/h, L/min, L/s, gal/min, mL/s, g/m³, kWh/100km, Wh/km, mi/kWh, km/kWh, in/d, mm/d``
|
||||
|
||||
Displaying the Unit of Measure in the Devices overview page can be disabled by unchecking the **Show Unit of Measure** checkbox on the Tools/Advanced page.
|
||||
|
||||
@@ -275,19 +275,19 @@ Available options, grouped per category:
|
||||
|
||||
**Basic**: ``Single``
|
||||
|
||||
**Environment**: ``Temp¹, Hum¹, Baro¹, Wind speed¹``
|
||||
**Environment**: ``Temp¹, Hum¹, Baro¹, Wind speed¹, Absolute humidity¹, Atmospheric pressure¹, Precipitation¹,`` ``Precipitation intensity¹``
|
||||
|
||||
**Dust/Gases**: ``Dust PM2.5¹, Dust PM1.0¹, Dust PM10¹, (e)CO2¹, TVOC¹, AQI¹, NOx¹``
|
||||
**Dust/Gases**: ``Dust PM2.5¹, Dust PM1.0¹, Dust PM10¹, (e)CO2¹, CO¹, TVOC¹, VOC parts¹, AQI¹, NOx¹,`` ``Gas¹, N2O¹, Ozone¹, SO2¹``
|
||||
|
||||
**Energy**: ``Voltage¹, Current¹, Power Usage¹, Power Factor¹, Apparent Power Usage¹, Reactive Power¹``
|
||||
**Energy**: ``Voltage¹, Current¹, Power Usage¹, Power Factor¹, Apparent Power Usage¹,`` ``Reactive Power¹, Reactive Energy¹, Energy¹, Energy storage¹, Energy distance¹``
|
||||
|
||||
**Time**: ``Duration¹, Date¹, Timestamp¹``
|
||||
|
||||
**Size**: ``Analog, Distance¹, Direction¹, Moisture¹, GPS, Weight¹, Data rate¹, Data size¹, Sound pressure¹, Signal strength¹``
|
||||
**Size**: ``Analog, Distance¹, Direction¹, Moisture¹, GPS, Weight¹, Data rate¹, Data size¹, Sound pressure¹, Signal strength¹,`` ``Volume¹, Volume flow rate¹, Volume storage¹, Water¹``
|
||||
|
||||
**Light**: ``Lux¹, UV¹, UV Index¹, IR¹, Red¹, Green¹, Blue¹, Color temperature¹``
|
||||
|
||||
**Other**: ``Switch¹, Switch (inv.)¹, Dimmer, String, UInt32 (1x), Int32 (1x), UInt64 (1x), Int64 (1x), Double (1x)``
|
||||
**Other**: ``Switch¹, Switch (inv.)¹, Dimmer, String, UInt32 (1x), Int32 (1x), UInt64 (1x), Int64 (1x), Double (1x), Frequency¹``
|
||||
|
||||
Value Types marked with ``¹`` are supported for use in MQTT AutoDiscovery.
|
||||
|
||||
@@ -299,6 +299,16 @@ Here's a partial preview (the 'None' value is selected):
|
||||
|
||||
.. image:: Task_config_page_ValueType_selector_part.png
|
||||
|
||||
MQTT State Class
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
For some Value Types a State Class can be provided in the MQTT Discovery configuration. By default this setting is empty, but for (mostly Energy related) this can be configured for:
|
||||
|
||||
* *Measurement*
|
||||
* *Measurement-angle*
|
||||
* *Total*
|
||||
* *Total-increasing*
|
||||
|
||||
|
|
||||
|
||||
.. _Plugin List:
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
.. |P078_status| replace:: :yellow:`ENERGY`
|
||||
.. |P078_github| replace:: P078_Eastron.ino
|
||||
.. _P078_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P078_Eastron.ino
|
||||
.. |P078_usedby| replace:: `Modbus variants of Eastron SDM120M, SDM120CT, SDM220M, SDM230M, SDM72M, SDM72CTM, SDM630, SDM630MCT, SDM630-EV, SDM54-M, SDM320Y, DDM18SD, SMART X-96, MCS-U22, TAiYEDQ TAC2100 `
|
||||
.. |P078_usedby| replace:: `Modbus variants of Eastron SDM120M, SDM120CT, SDM220M, SDM230M, SDM72M, SDM72CTM, SDM630, SDM630MCT, SDM630-EV, SDM54-M, SDM320Y, DDM18SD, SMART X-96, MCS-U22, TAiYEDQ TAC2100`
|
||||
.. |P078_shortinfo| replace:: `.`
|
||||
.. |P078_maintainer| replace:: TD-er
|
||||
.. |P078_compileinfo| replace:: `.`
|
||||
|
||||
@@ -119,11 +119,11 @@
|
||||
.. |P089_type| replace:: :cyan:`Communication`
|
||||
.. |P089_typename| replace:: :cyan:`Communication - Ping`
|
||||
.. |P089_porttype| replace:: `.`
|
||||
.. |P089_status| replace:: :yellow:`COLLECTION`
|
||||
.. |P089_status| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` :yellow:`ENERGY` :yellow:`NEOPIXEL`
|
||||
.. |P089_github| replace:: P089_Ping.ino
|
||||
.. _P089_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P089_Ping.ino
|
||||
.. |P089_usedby| replace:: `.`
|
||||
.. |P089_shortinfo| replace:: `.`
|
||||
.. |P089_maintainer| replace:: `TD-er`
|
||||
.. |P089_maintainer| replace:: `TD-er tonhuisman`
|
||||
.. |P089_compileinfo| replace:: `.`
|
||||
.. |P089_usedlibraries| replace:: `.`
|
||||
.. |P089_usedlibraries| replace:: `ESPping (ESP32)`
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,41 @@
|
||||
# ESPping
|
||||
Let the ESP32/ESP8266 ping a remote machine.
|
||||
|
||||
With this library an ESP32/ESP8266 can ping a remote machine and know if it's reachable.
|
||||
It provide some basic measurements on ping messages (avg response time).
|
||||
|
||||
thanks to Daniele Colanardi and Marian Craciunescu
|
||||
|
||||
## Usage
|
||||
|
||||
First, include the library in your sketch along with WiFi library:
|
||||
|
||||
```Arduino
|
||||
#include <ESPping.h>
|
||||
```
|
||||
|
||||
Next, simply call the `Ping.ping()` function
|
||||
|
||||
```Arduino
|
||||
IPAddress ip (192, 168, 0, 1); // The remote ip to ping
|
||||
bool ret = Ping.ping(ip);
|
||||
```
|
||||
|
||||
`ret` will be true if the remote responded to pings, false if not reachable.
|
||||
The library supports hostname too, just pass a string instead of the ip address:
|
||||
|
||||
```Arduino
|
||||
bool ret = Ping.ping("www.google.com");
|
||||
```
|
||||
|
||||
Additionally, the function accept a second integer parameter `count` that specify how many pings has to be sent:
|
||||
|
||||
```Arduino
|
||||
bool ret = Ping.ping(ip_or_host, 10);
|
||||
```
|
||||
|
||||
After `Ping.ping()` has been called, the average response time (in milliseconds) can be retrieved with
|
||||
|
||||
```Arduino
|
||||
float avg_time_ms = Ping.averageTime();
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
/******************************************************
|
||||
test Ping
|
||||
******************************************************/
|
||||
|
||||
#include <ESPping.h>
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
delay(100);
|
||||
|
||||
WiFi.begin("ssid","password");
|
||||
|
||||
// attente connexion
|
||||
Serial.print("\nConnection");
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.print("\nConnection OK, IP: ");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.print(" Gateway: ");
|
||||
Serial.println(WiFi.gatewayIP());
|
||||
Serial.println("You can try to ping me ;-)");
|
||||
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
// Ping IP
|
||||
const IPAddress remote_ip(9,9,9,9);
|
||||
Serial.print(remote_ip);
|
||||
if (Ping.ping(remote_ip) > 0){
|
||||
Serial.printf(" response time : %d/%.2f/%d ms\n", Ping.minTime(), Ping.averageTime(), Ping.maxTime());
|
||||
} else {
|
||||
Serial.println(" Error !");
|
||||
}
|
||||
delay(1000);
|
||||
|
||||
// Ping Host
|
||||
const char* remote_host = "quad9.net";
|
||||
Serial.print(remote_host);
|
||||
if (Ping.ping(remote_host) > 0){
|
||||
Serial.printf(" response time : %d/%.2f/%d ms\n", Ping.minTime(), Ping.averageTime(), Ping.maxTime());
|
||||
} else {
|
||||
Serial.println(" Ping Error !");
|
||||
}
|
||||
delay(1000);
|
||||
|
||||
// Ping local IP
|
||||
Serial.print(WiFi.gatewayIP());
|
||||
if (Ping.ping(WiFi.gatewayIP()) > 0){
|
||||
Serial.printf(" response time : %d/%.2f/%d ms\n", Ping.minTime(), Ping.averageTime(), Ping.maxTime());
|
||||
} else {
|
||||
Serial.println(" Ping Error !");
|
||||
}
|
||||
delay(1000);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For ESPping
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
ESPping KEYWORD3
|
||||
|
||||
#######################################
|
||||
# Class (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
Ping KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
ping KEYWORD2
|
||||
minTime KEYWORD2
|
||||
averageTime KEYWORD2
|
||||
maxTime KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
name=ESPping
|
||||
version=1.0.5
|
||||
author=dvarrel, Daniele Colanardi, Marian Craciunescu
|
||||
maintainer=dvarrel
|
||||
sentence=Let the ESP32/ESP8266 ping a remote machine.
|
||||
paragraph=With this library an ESP32/ESP8266 can ping a remote machine and know if reachable. It provide some basic measurements on ping messages (avg response time).
|
||||
category=Communication
|
||||
url=https://github.com/dvarrel/ESPping.git
|
||||
architectures=esp8266,esp32
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
ESP8266Ping - Ping library for ESP8266
|
||||
Copyright (c) 2015 Daniele Colanardi. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "ESPping.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef ESP8266
|
||||
extern "C" void esp_schedule();
|
||||
extern "C" void esp_yield();
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
extern "C" void esp_schedule(void) {};
|
||||
extern "C" void esp_yield(void) {};
|
||||
#endif
|
||||
|
||||
PingClass::PingClass() {}
|
||||
|
||||
bool PingClass::ping(IPAddress dest, int16_t count) {
|
||||
_expected_count = count;
|
||||
_errors = 0;
|
||||
_success = 0;
|
||||
|
||||
_min_time = INT_MAX;
|
||||
_avg_time = 0.0f;
|
||||
_max_time = 0;
|
||||
|
||||
|
||||
memset(&_options, 0, sizeof(struct ping_option));
|
||||
|
||||
// Repeat count (how many time send a ping message to destination)
|
||||
_options.count = count;
|
||||
// Time interval between two ping (seconds??)
|
||||
_options.coarse_time = 1;
|
||||
// Destination machine
|
||||
_options.ip = dest;
|
||||
|
||||
// Callbacks
|
||||
_options.recv_function = reinterpret_cast<ping_recv_function>(&PingClass::_ping_recv_cb);
|
||||
_options.sent_function = NULL; //reinterpret_cast<ping_sent_function>(&_ping_sent_cb);
|
||||
|
||||
// Let's go!
|
||||
if(ping_start(&_options)) {
|
||||
#ifdef ESP8266
|
||||
//wait until finished
|
||||
while((_success + _errors) < _expected_count){
|
||||
delay(1);
|
||||
esp_yield();
|
||||
};
|
||||
#else
|
||||
// Suspend till the process end
|
||||
esp_yield();
|
||||
#endif
|
||||
}
|
||||
return (_success > 0);
|
||||
}
|
||||
|
||||
bool PingClass::ping(const char* host, int16_t count) {
|
||||
IPAddress remote_addr;
|
||||
|
||||
if (WiFi.hostByName(host, remote_addr))
|
||||
return ping(remote_addr, count);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float PingClass::averageTime() {
|
||||
return _avg_time;
|
||||
}
|
||||
|
||||
float PingClass::minTime() {
|
||||
return _min_time;
|
||||
}
|
||||
|
||||
float PingClass::maxTime() {
|
||||
return _max_time;
|
||||
}
|
||||
|
||||
void PingClass::_ping_recv_cb(void *opt, void *resp) {
|
||||
// Cast the parameters to get some usable info
|
||||
ping_resp* ping_resp = reinterpret_cast<struct ping_resp*>(resp);
|
||||
//ping_option* ping_opt = reinterpret_cast<struct ping_option*>(opt);
|
||||
|
||||
// Error or success?
|
||||
#ifdef ESP8266
|
||||
if (ping_resp->ping_err == -1)
|
||||
_errors++;
|
||||
else {
|
||||
_success++;
|
||||
_avg_time += ping_resp->resp_time;
|
||||
if(ping_resp->resp_time < _min_time)
|
||||
_min_time = ping_resp->resp_time;
|
||||
if(ping_resp->resp_time > _max_time)
|
||||
_max_time = ping_resp->resp_time;
|
||||
|
||||
}
|
||||
#else
|
||||
_errors = ping_resp->ping_err;
|
||||
_success = _expected_count - _errors;
|
||||
_avg_time = ping_resp->resp_time;
|
||||
#endif
|
||||
|
||||
// Some debug info
|
||||
DEBUG_PING(
|
||||
"DEBUG: ping reply"
|
||||
"\ttotal_count = %d"
|
||||
"\tresp_time = %d"
|
||||
"\tseqno = %d"
|
||||
"\ttimeout_count = %d"
|
||||
"\tbytes = %d"
|
||||
"\ttotal_bytes = %d"
|
||||
"\ttotal_time = %d"
|
||||
"\tping_err = %d \n",
|
||||
(int)ping_resp->total_count, (int)ping_resp->resp_time, (int)ping_resp->seqno,
|
||||
(int)ping_resp->timeout_count, (int)ping_resp->bytes, (int)ping_resp->total_bytes,
|
||||
(int)ping_resp->total_time, (int)ping_resp->ping_err
|
||||
);
|
||||
|
||||
// Is it time to end?
|
||||
// Don't using seqno because it does not increase on error
|
||||
#ifdef ESP8266
|
||||
if (_success + _errors == _expected_count ) {
|
||||
_avg_time = _success > 0 ? _avg_time / _success : 0;
|
||||
#else
|
||||
if (ping_resp->total_count== _expected_count) {
|
||||
|
||||
#endif
|
||||
|
||||
DEBUG_PING("success %d errors %d\n", _success, _errors);
|
||||
DEBUG_PING("Resp times min %d, avg %.2f, max %d ms\n", _min_time, _avg_time, _max_time);
|
||||
|
||||
#ifdef ESP32
|
||||
// Done, return to main functiom
|
||||
esp_schedule();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
byte PingClass::_expected_count = 0;
|
||||
byte PingClass::_errors = 0;
|
||||
byte PingClass::_success = 0;
|
||||
float PingClass::_avg_time = 0;
|
||||
float PingClass::_min_time = std::numeric_limits<float>::max();
|
||||
float PingClass::_max_time = 0;
|
||||
|
||||
PingClass Ping;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
ESP32Ping - Ping library for ESP32 or ESP8266
|
||||
Copyright (c) 2018 Marian Craciunescu. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef ESPping_H
|
||||
#define ESPping_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#include <ping32.h>
|
||||
extern "C" {
|
||||
void esp_schedule(void);
|
||||
void esp_yield(void);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
extern "C" {
|
||||
#include <ping.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
//#define ENABLE_DEBUG_PING 0
|
||||
#ifdef ENABLE_DEBUG_PING
|
||||
#define DEBUG_PING(...) Serial.printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PING(...)
|
||||
#endif
|
||||
|
||||
class PingClass {
|
||||
public:
|
||||
PingClass();
|
||||
|
||||
bool ping(IPAddress dest, int16_t count = 5);
|
||||
bool ping(const char* host, int16_t count = 5);
|
||||
|
||||
float averageTime();
|
||||
float minTime();
|
||||
float maxTime();
|
||||
|
||||
protected:
|
||||
static void _ping_sent_cb(void *opt, void *pdata);
|
||||
static void _ping_recv_cb(void *opt, void *pdata);
|
||||
|
||||
IPAddress _dest;
|
||||
ping_option _options;
|
||||
|
||||
static byte _expected_count, _errors, _success;
|
||||
static float _min_time, _max_time;
|
||||
static float _avg_time;
|
||||
};
|
||||
|
||||
extern PingClass Ping;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
* ESP32 Ping library
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* and its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that the above copyright notice appear in all
|
||||
* copies and that both that the copyright notice and this
|
||||
* permission notice and warranty disclaimer appear in supporting
|
||||
* documentation, and that the name of the author not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
*
|
||||
* The author disclaim all warranties with regard to this
|
||||
* software, including all implied warranties of merchantability
|
||||
* and fitness. In no event shall the author be liable for any
|
||||
* special, indirect or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether
|
||||
* in an action of contract, negligence or other tortious action,
|
||||
* arising out of or in connection with the use or performance of
|
||||
* this software.
|
||||
*
|
||||
* --------------------------------------------------------------------------------
|
||||
* Ping Library is based on the following source code:
|
||||
*
|
||||
* Lua RTOS, ping utility
|
||||
*
|
||||
*
|
||||
* Author: Jaume Oliv� (jolive@iberoxarxa.com / jolive@whitecatboard.org)
|
||||
*
|
||||
* --------------------------------------------------------------------------------
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
*/
|
||||
#ifdef ESP32
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ping32.h"
|
||||
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/ip4.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/icmp.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
static uint16_t ping_seq_num;
|
||||
static uint8_t stopped = 0;
|
||||
|
||||
/*
|
||||
* Statistics
|
||||
*/
|
||||
static uint32_t transmitted = 0;
|
||||
static uint32_t received = 0;
|
||||
static float min_time = 0;
|
||||
static float max_time = 0;
|
||||
static float mean_time = 0;
|
||||
static float last_mean_time = 0;
|
||||
static float var_time = 0;
|
||||
|
||||
#define PING_ID 0xAFAF
|
||||
|
||||
#ifndef PING_DEFAULT_COUNT
|
||||
#define PING_DEFAULT_COUNT 10
|
||||
#endif
|
||||
#ifndef PING_DEFAULT_INTERVAL
|
||||
#define PING_DEFAULT_INTERVAL 1
|
||||
#endif
|
||||
#ifndef PING_DEFAULT_SIZE
|
||||
#define PING_DEFAULT_SIZE 32
|
||||
#endif
|
||||
#ifndef PING_DEFAULT_TIMEOUT
|
||||
#define PING_DEFAULT_TIMEOUT 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper functions
|
||||
*
|
||||
*/
|
||||
static void ping_prepare_echo(struct icmp_echo_hdr *iecho, uint16_t len) {
|
||||
size_t i;
|
||||
size_t data_len = len - sizeof(struct icmp_echo_hdr);
|
||||
|
||||
ICMPH_TYPE_SET(iecho, ICMP_ECHO);
|
||||
ICMPH_CODE_SET(iecho, 0);
|
||||
iecho->chksum = 0;
|
||||
iecho->id = PING_ID;
|
||||
iecho->seqno = htons(++ping_seq_num);
|
||||
|
||||
/* fill the additional data buffer with some data */
|
||||
for (i = 0; i < data_len; i++) {
|
||||
((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
|
||||
}
|
||||
|
||||
iecho->chksum = inet_chksum(iecho, len);
|
||||
}
|
||||
|
||||
static err_t ping_send(int s, ip4_addr_t *addr, int size) {
|
||||
struct icmp_echo_hdr *iecho;
|
||||
struct sockaddr_in to;
|
||||
size_t ping_size = sizeof(struct icmp_echo_hdr) + size;
|
||||
int err;
|
||||
|
||||
iecho = (struct icmp_echo_hdr *)mem_malloc((mem_size_t)ping_size);
|
||||
if (!iecho) {
|
||||
mem_free(iecho);
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
ping_prepare_echo(iecho, (uint16_t)ping_size);
|
||||
|
||||
to.sin_len = sizeof(to);
|
||||
to.sin_family = AF_INET;
|
||||
inet_addr_from_ip4addr(&to.sin_addr, addr);
|
||||
|
||||
if ((err = sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to)))) {
|
||||
transmitted++;
|
||||
}
|
||||
mem_free(iecho);
|
||||
return (err ? ERR_OK : ERR_VAL);
|
||||
}
|
||||
|
||||
static void ping_recv(int s) {
|
||||
char buf[64];
|
||||
int fromlen, len;
|
||||
struct sockaddr_in from;
|
||||
struct ip_hdr *iphdr;
|
||||
struct icmp_echo_hdr *iecho = NULL;
|
||||
char ipa[16];
|
||||
struct timeval begin;
|
||||
struct timeval end;
|
||||
uint64_t micros_begin;
|
||||
uint64_t micros_end;
|
||||
float elapsed;
|
||||
|
||||
// Register begin time
|
||||
gettimeofday(&begin, NULL);
|
||||
|
||||
// Send
|
||||
while ((len = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0) {
|
||||
if (len >= (int)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr))) {
|
||||
// Register end time
|
||||
gettimeofday(&end, NULL);
|
||||
|
||||
/// Get from IP address
|
||||
ip4_addr_t fromaddr;
|
||||
inet_addr_to_ip4addr(&fromaddr, &from.sin_addr);
|
||||
|
||||
strcpy(ipa, inet_ntoa(fromaddr));
|
||||
|
||||
// Get echo
|
||||
iphdr = (struct ip_hdr *)buf;
|
||||
iecho = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4));
|
||||
|
||||
// Print ....
|
||||
if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) {
|
||||
received++;
|
||||
|
||||
// Get elapsed time in milliseconds
|
||||
micros_begin = begin.tv_sec * 1000000;
|
||||
micros_begin += begin.tv_usec;
|
||||
|
||||
micros_end = end.tv_sec * 1000000;
|
||||
micros_end += end.tv_usec;
|
||||
|
||||
elapsed = (float)(micros_end - micros_begin) / (float)1000.0;
|
||||
|
||||
// Update statistics
|
||||
// Mean and variance are computed in an incremental way
|
||||
if (elapsed < min_time) {
|
||||
min_time = elapsed;
|
||||
}
|
||||
|
||||
if (elapsed > max_time) {
|
||||
max_time = elapsed;
|
||||
}
|
||||
|
||||
last_mean_time = mean_time;
|
||||
mean_time = (((received - 1) * mean_time) + elapsed) / received;
|
||||
|
||||
if (received > 1) {
|
||||
var_time = var_time + ((elapsed - last_mean_time) * (elapsed - mean_time));
|
||||
}
|
||||
|
||||
// Print ...
|
||||
log_d("%d bytes from %s: icmp_seq=%d time=%.3f ms", len, ipa,
|
||||
ntohs(iecho->seqno), elapsed
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
log_d("Request timeout for icmp_seq %d", ping_seq_num);
|
||||
}
|
||||
}
|
||||
/*
|
||||
static void stop_action(int i) {
|
||||
signal(i, SIG_DFL);
|
||||
|
||||
stopped = 1;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
* Operation functions
|
||||
*
|
||||
*/
|
||||
void ping(const char *name, int16_t count, int interval, int size, int timeout) {
|
||||
// Resolve name
|
||||
hostent * target = gethostbyname(name);
|
||||
IPAddress adr = *target->h_addr_list[0];
|
||||
if (target->h_length == 0) {
|
||||
// TODO: error not found target?????
|
||||
return;
|
||||
}
|
||||
ping_start(adr, count, interval, size, timeout);
|
||||
}
|
||||
|
||||
bool ping_start(struct ping_option *ping_o) {
|
||||
|
||||
|
||||
return ping_start(ping_o->ip,ping_o->count,0,0,0,ping_o);
|
||||
|
||||
}
|
||||
bool ping_start(IPAddress adr, int16_t count=0, int interval=0, int size=0, int timeout=0, struct ping_option *ping_o) {
|
||||
// driver_error_t *error;
|
||||
struct sockaddr_in address;
|
||||
ip4_addr_t ping_target;
|
||||
int s;
|
||||
// Get default values if argument are not provided
|
||||
if (count == 0) {
|
||||
count = PING_DEFAULT_COUNT;
|
||||
}
|
||||
|
||||
if (interval == 0) {
|
||||
interval = PING_DEFAULT_INTERVAL;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
size = PING_DEFAULT_SIZE;
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
timeout = PING_DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
// Create socket
|
||||
if ((s = socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) {
|
||||
// TODO: error
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
address.sin_addr.s_addr = adr;
|
||||
ping_target.addr = address.sin_addr.s_addr;
|
||||
|
||||
// Setup socket
|
||||
struct timeval tout;
|
||||
|
||||
// Timeout
|
||||
tout.tv_sec = timeout;
|
||||
tout.tv_usec = 0;
|
||||
|
||||
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(tout)) < 0) {
|
||||
closesocket(s);
|
||||
// TODO: error
|
||||
return false;
|
||||
}
|
||||
|
||||
stopped = 0;
|
||||
transmitted = 0;
|
||||
received = 0;
|
||||
min_time = 1.E+9;// FLT_MAX;
|
||||
max_time = 0.0;
|
||||
mean_time = 0.0;
|
||||
var_time = 0.0;
|
||||
|
||||
// Register signal for stop ping
|
||||
//signal(SIGINT, stop_action);
|
||||
|
||||
// Begin ping ...
|
||||
char ipa[16];
|
||||
|
||||
strcpy(ipa, inet_ntoa(ping_target));
|
||||
log_i("PING %s: %d data bytes", ipa, size);
|
||||
|
||||
ping_seq_num = 0;
|
||||
|
||||
unsigned long ping_started_time = millis();
|
||||
while ((ping_seq_num < count) && (!stopped)) {
|
||||
if (ping_send(s, &ping_target, size) == ERR_OK) {
|
||||
ping_recv(s);
|
||||
}
|
||||
if(ping_seq_num < count){
|
||||
delay( interval*1000L);
|
||||
}
|
||||
}
|
||||
|
||||
closesocket(s);
|
||||
|
||||
log_i("%d packets transmitted, %d packets received, %.1f%% packet loss",
|
||||
transmitted,
|
||||
received,
|
||||
((((float)transmitted - (float)received) / (float)transmitted) * 100.0)
|
||||
);
|
||||
|
||||
|
||||
if (ping_o) {
|
||||
ping_resp pingresp;
|
||||
log_i("round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms", min_time, mean_time, max_time, sqrt(var_time / received));
|
||||
pingresp.total_count = count; //Number of pings
|
||||
pingresp.resp_time = mean_time; //Average time for the pings
|
||||
pingresp.seqno = 0; //not relevant
|
||||
pingresp.timeout_count = transmitted - received; //number of pings which failed
|
||||
pingresp.bytes = size; //number of bytes received for 1 ping
|
||||
pingresp.total_bytes = size * count; //number of bytes for all pings
|
||||
pingresp.total_time = (millis() - ping_started_time) / 1000.0; //Time consumed for all pings; it takes into account also timeout pings
|
||||
pingresp.ping_err = transmitted - received; //number of pings failed
|
||||
// Call the callback function
|
||||
ping_o->recv_function(ping_o, &pingresp);
|
||||
}
|
||||
|
||||
// Return true if at least one ping had a successfull "pong"
|
||||
return (received > 0);
|
||||
}
|
||||
|
||||
bool ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv)
|
||||
{
|
||||
if (ping_opt == NULL)
|
||||
return false;
|
||||
|
||||
ping_opt->recv_function = ping_recv;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ping_regist_sent(struct ping_option *ping_opt, ping_sent_function ping_sent)
|
||||
{
|
||||
if (ping_opt == NULL)
|
||||
return false;
|
||||
|
||||
ping_opt->sent_function = ping_sent;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifdef ESP32
|
||||
|
||||
#ifndef PING32_H
|
||||
#define PING32_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef void(*ping_recv_function)(void* arg, void *pdata);
|
||||
typedef void(*ping_sent_function)(void* arg, void *pdata);
|
||||
|
||||
struct ping_option {
|
||||
int16_t count;
|
||||
uint32_t ip;
|
||||
uint32_t coarse_time;
|
||||
ping_recv_function recv_function;
|
||||
ping_sent_function sent_function;
|
||||
void* reverse;
|
||||
};
|
||||
|
||||
struct ping_resp {
|
||||
uint32_t total_count;
|
||||
float resp_time;
|
||||
uint32_t seqno;
|
||||
uint32_t timeout_count;
|
||||
uint32_t bytes;
|
||||
uint32_t total_bytes;
|
||||
float total_time;
|
||||
int8_t ping_err;
|
||||
};
|
||||
|
||||
bool ping_start(struct ping_option *ping_opt);
|
||||
void ping(const char *name, int16_t count, int interval, int size, int timeout);
|
||||
bool ping_start(IPAddress adr, int16_t count, int interval, int size, int timeout, struct ping_option *ping_o = NULL);
|
||||
|
||||
#endif
|
||||
#endif // PING_H
|
||||
@@ -214,8 +214,14 @@ bool CPlugin_002(CPlugin::Function function, struct EventStruct *event, String&
|
||||
String pubname = CPlugin_002_pubname;
|
||||
parseControllerVariables(pubname, event, false);
|
||||
|
||||
const bool taskRetained = Settings.SendRetainedTaskValues(event->TaskIndex, event->ControllerIndex);
|
||||
|
||||
// Publish using move operator, thus pubname and json are empty after this call
|
||||
success = MQTTpublish(event->ControllerIndex, event->TaskIndex, std::move(pubname), std::move(json), CPlugin_002_mqtt_retainFlag);
|
||||
success = MQTTpublish(event->ControllerIndex,
|
||||
event->TaskIndex,
|
||||
std::move(pubname),
|
||||
std::move(json),
|
||||
CPlugin_002_mqtt_retainFlag || taskRetained);
|
||||
} // if idx !=0
|
||||
else
|
||||
{
|
||||
|
||||
@@ -191,9 +191,9 @@ bool CPlugin_005(CPlugin::Function function, struct EventStruct *event, String&
|
||||
break;
|
||||
}
|
||||
|
||||
// String pubname = CPlugin_005_pubname;
|
||||
const bool taskRetained = Settings.SendRetainedTaskValues(event->TaskIndex, event->ControllerIndex);
|
||||
|
||||
success = MQTT_protocol_send(event, CPlugin_005_pubname, CPlugin_005_mqtt_retainFlag);
|
||||
success = MQTT_protocol_send(event, CPlugin_005_pubname, CPlugin_005_mqtt_retainFlag || taskRetained);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
* 2023-03-15 tonhuisman: Handle setting payload to (Dummy) Devices via topic SysName/TaskName/ValueName/set
|
||||
* 2023-03 Changelog started
|
||||
*/
|
||||
|
||||
// # include "src/Commands/InternalCommands.h"
|
||||
# include "src/Commands/ExecuteCommand.h"
|
||||
# include "src/ESPEasyCore/Controller.h"
|
||||
@@ -44,9 +45,9 @@ bool CPlugin_006(CPlugin::Function function, struct EventStruct *event, String&
|
||||
proto.usesExtCreds = true;
|
||||
proto.defaultPort = 1883;
|
||||
proto.usesID = false;
|
||||
#if FEATURE_MQTT_TLS
|
||||
proto.usesTLS = true;
|
||||
#endif
|
||||
# if FEATURE_MQTT_TLS
|
||||
proto.usesTLS = true;
|
||||
# endif // if FEATURE_MQTT_TLS
|
||||
# if FEATURE_STRING_VARIABLES
|
||||
proto.allowSendDerived = true;
|
||||
# endif // if FEATURE_STRING_VARIABLES
|
||||
@@ -130,7 +131,9 @@ bool CPlugin_006(CPlugin::Function function, struct EventStruct *event, String&
|
||||
break;
|
||||
}
|
||||
|
||||
success = MQTT_protocol_send(event, CPlugin_006_pubname, CPlugin_006_mqtt_retainFlag);
|
||||
const bool taskRetained = Settings.SendRetainedTaskValues(event->TaskIndex, event->ControllerIndex);
|
||||
|
||||
success = MQTT_protocol_send(event, CPlugin_006_pubname, CPlugin_006_mqtt_retainFlag || taskRetained);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -809,7 +809,9 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String&
|
||||
break;
|
||||
}
|
||||
|
||||
success = MQTT_protocol_send(event, CPlugin_014_pubname, CPlugin_014_mqtt_retainFlag);
|
||||
const bool taskRetained = Settings.SendRetainedTaskValues(event->TaskIndex, event->ControllerIndex);
|
||||
|
||||
success = MQTT_protocol_send(event, CPlugin_014_pubname, CPlugin_014_mqtt_retainFlag || taskRetained);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ boolean Plugin_026(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.OutputDataType = Output_Data_type_t::Simple;
|
||||
dev.PluginStats = true;
|
||||
dev.CustomVTypeVar = true;
|
||||
dev.MqttStateClass = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ boolean Plugin_033(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.OutputDataType = Output_Data_type_t::All;
|
||||
dev.PluginStats = true;
|
||||
dev.CustomVTypeVar = true;
|
||||
dev.MqttStateClass = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
// This task reads data from the MQTT Import input stream and saves the value
|
||||
|
||||
/**
|
||||
* 2025-08-20 tonhuisman: Generate events with numeric values using the configured decimals setting.
|
||||
* 2025-06-14 tonhuisman: Add support for Custom Value Type per task value
|
||||
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for MQTT Import)
|
||||
* Update changelog
|
||||
@@ -643,10 +644,10 @@ boolean Plugin_037(uint8_t function, struct EventStruct *event, String& string)
|
||||
# if !defined(P037_LIMIT_BUILD_SIZE) || defined(P037_OVERRIDE)
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("IMPT : [%s#%s] : %.3f"),
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("IMPT : [%s#%s] : %s"),
|
||||
getTaskDeviceName(event->TaskIndex).c_str(),
|
||||
checkJson ? key.c_str() : getTaskValueName(event->TaskIndex, x).c_str(),
|
||||
doublePayload));
|
||||
toString(doublePayload, ExtraTaskSettings.TaskDeviceValueDecimals[x]).c_str()));
|
||||
}
|
||||
# endif // if !defined(P037_LIMIT_BUILD_SIZE) || defined(P037_OVERRIDE)
|
||||
|
||||
@@ -687,7 +688,7 @@ boolean Plugin_037(uint8_t function, struct EventStruct *event, String& string)
|
||||
RuleEvent += '=';
|
||||
|
||||
if (numericPayload) {
|
||||
RuleEvent += doublePayload;
|
||||
RuleEvent += toString(doublePayload, ExtraTaskSettings.TaskDeviceValueDecimals[x]);
|
||||
} else {
|
||||
RuleEvent += wrapWithQuotesIfContainsParameterSeparatorChar(Payload);
|
||||
}
|
||||
|
||||
@@ -11,26 +11,21 @@
|
||||
|
||||
This plugin reads the particle concentration from SDS011 Sensor
|
||||
DevicePin1 - RX on ESP, TX on SDS
|
||||
DevicePin2 - TX on ESP, RX on SDS, optional, for setting the sleep time
|
||||
*/
|
||||
|
||||
/** Changelog:
|
||||
* 2025-08-05 tonhuisman: Introduce multi-instance use
|
||||
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery
|
||||
*/
|
||||
|
||||
// #ifdef ESP8266 // Needed for precompile issues.
|
||||
|
||||
# define PLUGIN_056
|
||||
# define PLUGIN_ID_056 56
|
||||
# define PLUGIN_NAME_056 "Dust - SDS011/018/198"
|
||||
# define PLUGIN_VALUENAME1_056 "PM2.5" // Dust <2.5µm in µg/m³ SDS198:<100µm in µg/m³
|
||||
# define PLUGIN_VALUENAME2_056 "PM10" // Dust <10µm in µg/m³
|
||||
|
||||
# include <jkSDS011.h>
|
||||
|
||||
# include "ESPEasy-Globals.h"
|
||||
|
||||
|
||||
CjkSDS011 *Plugin_056_SDS = nullptr;
|
||||
# include "src/PluginStructs/P056_data_struct.h"
|
||||
|
||||
|
||||
boolean Plugin_056(uint8_t function, struct EventStruct *event, String& string)
|
||||
@@ -50,6 +45,9 @@ boolean Plugin_056(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.SendDataOption = true;
|
||||
dev.TimerOption = true;
|
||||
dev.PluginStats = true;
|
||||
|
||||
// Keep alive for setting the sleep time on save, only when changed
|
||||
dev.ExitTaskBeforeSave = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -109,7 +107,7 @@ boolean Plugin_056(uint8_t function, struct EventStruct *event, String& string)
|
||||
|
||||
if (PCONFIG(0) != newsleeptime) {
|
||||
PCONFIG(0) = newsleeptime;
|
||||
Plugin_056_setWorkingPeriod(newsleeptime);
|
||||
Plugin_056_setWorkingPeriod(event, newsleeptime);
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
@@ -118,45 +116,34 @@ boolean Plugin_056(uint8_t function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
if (Plugin_056_SDS) {
|
||||
delete Plugin_056_SDS;
|
||||
}
|
||||
initPluginTaskData(event->TaskIndex, new (std::nothrow) P056_data_struct(event));
|
||||
P056_data_struct *P056_data =
|
||||
static_cast<P056_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
success = (nullptr != P056_data && P056_data->isInitialized());
|
||||
|
||||
const ESPEasySerialPort port = static_cast<ESPEasySerialPort>(CONFIG_PORT);
|
||||
Plugin_056_SDS = new (std::nothrow) CjkSDS011(port, CONFIG_PIN1, CONFIG_PIN2);
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("SDS : Init OK ESP GPIO-pin RX:%d TX:%d"), CONFIG_PIN1, CONFIG_PIN2));
|
||||
}
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_EXIT:
|
||||
{
|
||||
// //FIXME: if this plugin is used more than once at the same time, things go horribly wrong :)
|
||||
// FIXME TD-er: Must implement plugin_data_struct for this
|
||||
//
|
||||
// if (Plugin_056_SDS)
|
||||
// delete Plugin_056_SDS;
|
||||
// addLog(LOG_LEVEL_INFO, F("SDS : Exit"));
|
||||
shouldReboot = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_FIFTY_PER_SECOND:
|
||||
{
|
||||
if (!Plugin_056_SDS) {
|
||||
P056_data_struct *P056_data =
|
||||
static_cast<P056_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
|
||||
if ((nullptr == P056_data) || !P056_data->isInitialized()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Plugin_056_SDS->Process();
|
||||
P056_data->Process();
|
||||
|
||||
if (Plugin_056_SDS->available())
|
||||
if (P056_data->available())
|
||||
{
|
||||
const float pm2_5 = Plugin_056_SDS->GetPM2_5();
|
||||
const float pm10 = Plugin_056_SDS->GetPM10_();
|
||||
const float pm2_5 = P056_data->GetPM2_5();
|
||||
const float pm10 = P056_data->GetPM10_();
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
@@ -179,13 +166,17 @@ boolean Plugin_056(uint8_t function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
if (!Plugin_056_SDS) {
|
||||
P056_data_struct *P056_data =
|
||||
static_cast<P056_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
|
||||
if ((nullptr == P056_data) || !P056_data->isInitialized()) {
|
||||
break;
|
||||
}
|
||||
|
||||
float pm25, pm10;
|
||||
float pm25{};
|
||||
float pm10{};
|
||||
|
||||
if (Plugin_056_SDS->ReadAverage(pm25, pm10)) {
|
||||
if (P056_data->ReadAverage(pm25, pm10)) {
|
||||
UserVar.setFloat(event->TaskIndex, 0, pm25);
|
||||
UserVar.setFloat(event->TaskIndex, 1, pm10);
|
||||
success = true;
|
||||
@@ -220,11 +211,14 @@ String Plugin_056_WorkingPeriodToString(int workingPeriod) {
|
||||
return log;
|
||||
}
|
||||
|
||||
void Plugin_056_setWorkingPeriod(int minutes) {
|
||||
if (!Plugin_056_SDS) {
|
||||
void Plugin_056_setWorkingPeriod(EventStruct *event, int minutes) {
|
||||
P056_data_struct *P056_data =
|
||||
static_cast<P056_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
|
||||
if ((nullptr == P056_data) || !P056_data->isInitialized()) {
|
||||
return;
|
||||
}
|
||||
Plugin_056_SDS->SetWorkingPeriod(minutes);
|
||||
P056_data->SetWorkingPeriod(minutes);
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, concat(F("SDS : Working Period set to: "), Plugin_056_WorkingPeriodToString(minutes)));
|
||||
|
||||
@@ -230,6 +230,7 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.PluginStats = true;
|
||||
dev.setPin1Direction(gpio_direction::gpio_output);
|
||||
dev.OutputDataType = Output_Data_type_t::Simple;
|
||||
dev.MqttStateClass = true;
|
||||
|
||||
// dev.TaskLogsOwnPeaks = true;
|
||||
break;
|
||||
@@ -953,7 +954,7 @@ int Plugin_076_QueryVType(uint8_t value_nr) {
|
||||
case P076_INDEX_PF:
|
||||
result = Sensor_VType::SENSOR_TYPE_POWER_FACT_ONLY; break;
|
||||
case P076_INDEX_ENER:
|
||||
result = Sensor_VType::SENSOR_TYPE_NONE; break; // FIXME Add support for Energy + Unit of Measure
|
||||
result = Sensor_VType::SENSOR_TYPE_ENERGY; break;
|
||||
}
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ boolean Plugin_077(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.TimerOptional = true;
|
||||
dev.PluginStats = true;
|
||||
dev.TaskLogsOwnPeaks = true;
|
||||
dev.MqttStateClass = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ boolean Plugin_077(uint8_t function, struct EventStruct *event, String& string)
|
||||
# if FEATURE_MQTT_DISCOVER
|
||||
case PLUGIN_GET_DISCOVERY_VTYPES:
|
||||
{
|
||||
success = getDiscoveryVType(event, Plugin_077_QueryVType, P077_QUERY1_CONFIG_POS, event->Par5);;
|
||||
success = getDiscoveryVType(event, Plugin_077_QueryVType, P077_QUERY1_CONFIG_POS, event->Par5);
|
||||
break;
|
||||
}
|
||||
# endif // if FEATURE_MQTT_DISCOVER
|
||||
|
||||
@@ -58,6 +58,7 @@ boolean Plugin_078(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.TimerOption = true;
|
||||
dev.PluginStats = true;
|
||||
dev.TaskLogsOwnPeaks = true;
|
||||
dev.MqttStateClass = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -194,12 +195,12 @@ boolean Plugin_078(uint8_t function, struct EventStruct *event, String& string)
|
||||
F("SDM630"),
|
||||
F("SDM72_V2"),
|
||||
F("SDM320C"),
|
||||
F("TAC2100")
|
||||
F("TAC2100"),
|
||||
};
|
||||
constexpr size_t nrOptions = NR_ELEMENTS(options_model);
|
||||
const FormSelectorOptions selector(nrOptions, options_model);
|
||||
FormSelectorOptions selector(nrOptions, options_model);
|
||||
selector.reloadonchange = true;
|
||||
selector.addFormSelector(F("Model Type"), P078_MODEL_LABEL, P078_MODEL);
|
||||
addFormNote(F("Submit after changing the modell to update Output Configuration."));
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
|
||||
@@ -43,6 +43,7 @@ boolean Plugin_085(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.TimerOption = true;
|
||||
dev.ExitTaskBeforeSave = false;
|
||||
dev.PluginStats = true;
|
||||
dev.MqttStateClass = true;
|
||||
dev.setPin3Direction(gpio_direction::gpio_output);
|
||||
break;
|
||||
}
|
||||
@@ -74,7 +75,7 @@ boolean Plugin_085(uint8_t function, struct EventStruct *event, String& string)
|
||||
# if FEATURE_MQTT_DISCOVER
|
||||
case PLUGIN_GET_DISCOVERY_VTYPES:
|
||||
{
|
||||
success = getDiscoveryVType(event, Plugin_085_QueryVType, P085_QUERY1_CONFIG_POS, event->Par5);;
|
||||
success = getDiscoveryVType(event, Plugin_085_QueryVType, P085_QUERY1_CONFIG_POS, event->Par5);
|
||||
break;
|
||||
}
|
||||
# endif // if FEATURE_MQTT_DISCOVER
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "_Plugin_Helper.h"
|
||||
#if defined(USES_P089) && defined(ESP8266)
|
||||
#ifdef USES_P089
|
||||
|
||||
# include "src/PluginStructs/P089_data_struct.h"
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
Maintainer: Denys Fedoryshchenko, denys AT nuclearcat.com
|
||||
*/
|
||||
/** Changelog:
|
||||
* 2025-08-15 tonhuisman: Move actual ping action to a service, instantiated by the first task instance, and deleted by the last stopped
|
||||
* task instance. Service is looped in 10 per second function.
|
||||
* Enable PluginStats for ESP32, so the trend of average ping-time can be displayed.
|
||||
* 2025-08-15 tonhuisman: Reduce max. ping count from 100 to 25
|
||||
* 2025-08-14 tonhuisman: Add code to plugin to use dvarrel/ESPping library that works for ESP32.
|
||||
* ESP32 handles the ping in an independent RTOS task, to make it non-blocking.
|
||||
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Ping)
|
||||
* 2023-03-19 tonhuisman: Show hostname in GPIO column of Devices page
|
||||
* 2023-03-14 tonhuisman: Change command handling to not require the taskname as the second argument if no 3rd argument is given.
|
||||
@@ -30,6 +36,7 @@
|
||||
# define PLUGIN_089
|
||||
# define PLUGIN_NAME_089 "Communication - Ping"
|
||||
# define PLUGIN_VALUENAME1_089 "Fails"
|
||||
# define PLUGIN_VALUENAME2_089 "Avg_ms"
|
||||
|
||||
|
||||
boolean Plugin_089(uint8_t function, struct EventStruct *event, String& string)
|
||||
@@ -48,6 +55,9 @@ boolean Plugin_089(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.DecimalsOnly = true;
|
||||
dev.SendDataOption = true;
|
||||
dev.TimerOption = true;
|
||||
# ifdef ESP32
|
||||
dev.PluginStats = true;
|
||||
# endif // ifdef ESP32
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -60,14 +70,34 @@ boolean Plugin_089(uint8_t function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_089));
|
||||
# ifdef ESP32
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_089));
|
||||
# endif // ifdef ESP32
|
||||
break;
|
||||
}
|
||||
|
||||
# ifdef ESP32
|
||||
case PLUGIN_GET_DEVICEVALUECOUNT:
|
||||
{
|
||||
int vCount = P089_VALUE_COUNT;
|
||||
|
||||
if (0 == vCount) {
|
||||
vCount = 1;
|
||||
}
|
||||
event->Par1 = vCount;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
# endif // ifdef ESP32
|
||||
|
||||
# if FEATURE_MQTT_DISCOVER
|
||||
case PLUGIN_GET_DISCOVERY_VTYPES:
|
||||
{
|
||||
event->Par1 = static_cast<int>(Sensor_VType::SENSOR_TYPE_NONE); // Not yet supported
|
||||
success = true;
|
||||
# ifdef ESP32
|
||||
event->Par2 = static_cast<int>(Sensor_VType::SENSOR_TYPE_DURATION);
|
||||
# endif // ifdef ESP32
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
# endif // if FEATURE_MQTT_DISCOVER
|
||||
@@ -92,12 +122,35 @@ boolean Plugin_089(uint8_t function, struct EventStruct *event, String& string)
|
||||
char hostname[PLUGIN_089_HOSTNAME_SIZE]{};
|
||||
LoadCustomTaskSettings(event->TaskIndex, (uint8_t *)&hostname, PLUGIN_089_HOSTNAME_SIZE);
|
||||
addFormTextBox(F("Hostname"), F("host"), hostname, PLUGIN_089_HOSTNAME_SIZE - 2);
|
||||
|
||||
# ifdef ESP32
|
||||
|
||||
if (P089_PING_COUNT < 1) {
|
||||
P089_PING_COUNT = 5;
|
||||
}
|
||||
addFormNumericBox(F("Ping count"), F("pcount"), P089_PING_COUNT, 1, P089_MAX_PING_COUNT);
|
||||
|
||||
const __FlashStringHelper*countOptions[] = {
|
||||
F("Fails"),
|
||||
F("Fails, Avg_ms"),
|
||||
};
|
||||
const int countValues[] = { 1, 2 };
|
||||
FormSelectorOptions selector(2, countOptions, countValues);
|
||||
selector.reloadonchange = true;
|
||||
selector.addFormSelector(F("Available Values"), F("pvals"), P089_VALUE_COUNT);
|
||||
# endif // ifdef ESP32
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
# ifdef ESP32
|
||||
P089_PING_COUNT = getFormItemInt(F("pcount"), 5);
|
||||
P089_VALUE_COUNT = getFormItemInt(F("pvals"), 1);
|
||||
# endif // ifdef ESP32
|
||||
|
||||
char hostname[PLUGIN_089_HOSTNAME_SIZE]{};
|
||||
|
||||
// Reset "Fails" if settings updated
|
||||
@@ -112,15 +165,30 @@ boolean Plugin_089(uint8_t function, struct EventStruct *event, String& string)
|
||||
{
|
||||
success = initPluginTaskData(event->TaskIndex, new (std::nothrow) P089_data_struct());
|
||||
UserVar.setFloat(event->TaskIndex, 0, 0);
|
||||
# ifdef ESP32
|
||||
UserVar.setFloat(event->TaskIndex, 1, 0);
|
||||
# endif // ifdef ESP32
|
||||
break;
|
||||
}
|
||||
|
||||
# ifdef ESP32
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
P089_data_struct *P089_taskdata =
|
||||
static_cast<P089_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
|
||||
if ((nullptr != P089_taskdata) && P089_taskdata->isInitialized()) {
|
||||
success = P089_taskdata->loop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
# endif // ifdef ESP32
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
P089_data_struct *P089_taskdata =
|
||||
static_cast<P089_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
|
||||
if (nullptr == P089_taskdata) {
|
||||
if ((nullptr == P089_taskdata) || !P089_taskdata->isInitialized()) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -165,4 +233,4 @@ boolean Plugin_089(uint8_t function, struct EventStruct *event, String& string)
|
||||
return success;
|
||||
}
|
||||
|
||||
#endif // if defined(USES_P089) && defined(ESP8266)
|
||||
#endif // ifdef USES_P089
|
||||
|
||||
@@ -55,6 +55,7 @@ boolean Plugin_093(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.TimerOption = true;
|
||||
dev.TimerOptional = true;
|
||||
dev.CustomVTypeVar = true;
|
||||
dev.MqttStateClass = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ boolean Plugin_102(uint8_t function, struct EventStruct *even
|
||||
dev.OutputDataType = Output_Data_type_t::Simple;
|
||||
dev.SendDataOption = true;
|
||||
dev.TimerOption = true;
|
||||
dev.MqttStateClass = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -405,9 +406,9 @@ int Plugin_102_QueryVType(uint8_t query) {
|
||||
case 0: result = Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY; break;
|
||||
case 1: result = Sensor_VType::SENSOR_TYPE_CURRENT_ONLY; break;
|
||||
case 2: result = Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY; break;
|
||||
case 3: result = Sensor_VType::SENSOR_TYPE_NONE; break; // FIXME
|
||||
case 3: result = Sensor_VType::SENSOR_TYPE_ENERGY; break;
|
||||
case 4: result = Sensor_VType::SENSOR_TYPE_POWER_FACT_ONLY; break;
|
||||
case 5: result = Sensor_VType::SENSOR_TYPE_NONE; break;
|
||||
case 5: result = Sensor_VType::SENSOR_TYPE_FREQUENCY; break;
|
||||
}
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ boolean Plugin_108(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.SendDataOption = true;
|
||||
dev.TimerOption = true;
|
||||
dev.PluginStats = true;
|
||||
dev.MqttStateClass = true;
|
||||
dev.setPin3Direction(gpio_direction::gpio_output);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
// #######################################################################################################
|
||||
|
||||
/**
|
||||
* 2025-08-21 tonhuisman: Remove Lux Read-Method options without 'no wait' as that causes massive delays, disturbing other ESPEasy
|
||||
* operations
|
||||
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery
|
||||
* 2024-06-21 tonhuisman: Fix support for VEML6030, using by default the alternate I2C address, by modifying the VEML7700 library
|
||||
* 2024-05-18 tonhuisman: Implement AutoLux feature, and Get Config Value for automatically determined gain and integration
|
||||
@@ -95,7 +97,7 @@ boolean Plugin_168(uint8_t function, struct EventStruct *event, String& string)
|
||||
|
||||
case PLUGIN_SET_DEFAULTS:
|
||||
{
|
||||
P168_READLUX_MODE = VEML_LUX_AUTO;
|
||||
P168_READLUX_MODE = VEML_LUX_CORRECTED_NOWAIT;
|
||||
P168_PSM_MODE = static_cast<int>(P168_power_save_mode_e::Disabled);
|
||||
|
||||
ExtraTaskSettings.TaskDeviceValueDecimals[1] = 0; // White
|
||||
@@ -108,24 +110,34 @@ boolean Plugin_168(uint8_t function, struct EventStruct *event, String& string)
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
{
|
||||
uint8_t _readMethod = P168_READLUX_MODE;
|
||||
|
||||
if (static_cast<uint8_t>(VEML_LUX_NORMAL) == _readMethod) {
|
||||
_readMethod = static_cast<uint8_t>(VEML_LUX_NORMAL_NOWAIT);
|
||||
} else if ((static_cast<uint8_t>(VEML_LUX_CORRECTED) == _readMethod) || (static_cast<uint8_t>(VEML_LUX_AUTO) == _readMethod)) {
|
||||
_readMethod = static_cast<uint8_t>(VEML_LUX_CORRECTED_NOWAIT);
|
||||
}
|
||||
P168_READLUX_MODE = _readMethod;
|
||||
|
||||
const __FlashStringHelper *readMethod[] = {
|
||||
F("Normal"),
|
||||
F("Corrected"),
|
||||
F("Auto"),
|
||||
// F("Normal"),
|
||||
// F("Corrected"),
|
||||
// F("Auto"),
|
||||
F("Normal (no wait)"),
|
||||
F("Corrected (no wait)"),
|
||||
};
|
||||
const int readMethodOptions[] = {
|
||||
VEML_LUX_NORMAL,
|
||||
VEML_LUX_CORRECTED,
|
||||
VEML_LUX_AUTO,
|
||||
// VEML_LUX_NORMAL,
|
||||
// VEML_LUX_CORRECTED,
|
||||
// VEML_LUX_AUTO,
|
||||
VEML_LUX_NORMAL_NOWAIT,
|
||||
VEML_LUX_CORRECTED_NOWAIT,
|
||||
};
|
||||
constexpr size_t optionCount = NR_ELEMENTS(readMethodOptions);
|
||||
const FormSelectorOptions selector(optionCount, readMethod, readMethodOptions);
|
||||
selector.addFormSelector(F("Lux Read-method"), F("rmth"), P168_READLUX_MODE);
|
||||
addFormNote(F("For 'Auto' Read-method, the Gain factor and Integration time settings are ignored."));
|
||||
|
||||
// addFormNote(F("For 'Auto' Read-method, the Gain factor and Integration time settings are ignored."));
|
||||
}
|
||||
{
|
||||
const __FlashStringHelper *alsGain[] = {
|
||||
|
||||
@@ -58,6 +58,7 @@ boolean Plugin_176(uint8_t function, struct EventStruct *event, String& string)
|
||||
dev.TimerOptional = true;
|
||||
dev.PluginStats = true;
|
||||
dev.CustomVTypeVar = true;
|
||||
dev.MqttStateClass = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1753,6 +1753,9 @@ To create/register a plugin, you have to :
|
||||
#ifndef USES_P085
|
||||
#define USES_P085 // AcuDC24x
|
||||
#endif
|
||||
#ifndef USES_P089
|
||||
#define USES_P089 // Ping
|
||||
#endif
|
||||
#ifndef USES_P093
|
||||
#define USES_P093 // Mitsubishi Heat Pump
|
||||
#endif
|
||||
@@ -1977,6 +1980,9 @@ To create/register a plugin, you have to :
|
||||
#ifndef USES_P083
|
||||
#define USES_P083 // SGP30
|
||||
#endif
|
||||
#ifndef USES_P089
|
||||
#define USES_P089 // Ping
|
||||
#endif
|
||||
#ifndef USES_P090
|
||||
#define USES_P090 // CCS811
|
||||
#endif
|
||||
@@ -2094,6 +2100,9 @@ To create/register a plugin, you have to :
|
||||
#ifndef USES_P070
|
||||
#define USES_P070 // NeoPixel_Clock
|
||||
#endif
|
||||
#ifndef USES_P089
|
||||
#define USES_P089 // Ping
|
||||
#endif
|
||||
#ifndef USES_P128
|
||||
#define USES_P128 // NeoPixelBusFX
|
||||
#endif
|
||||
@@ -2326,6 +2335,9 @@ To create/register a plugin, you have to :
|
||||
#ifndef USES_P088
|
||||
#define USES_P088 // ToniA IR plugin
|
||||
#endif
|
||||
#ifndef USES_P089
|
||||
#define USES_P089 // Ping
|
||||
#endif
|
||||
#ifndef USES_P094
|
||||
#define USES_P094 // CUL Reader
|
||||
#endif
|
||||
@@ -3136,6 +3148,18 @@ To create/register a plugin, you have to :
|
||||
#define FEATURE_MQTT_DEVICECLASS 0
|
||||
#endif
|
||||
|
||||
#if !defined(FEATURE_MQTT_STATE_CLASS) && FEATURE_MQTT
|
||||
#if defined(LIMIT_BUILD_SIZE) || defined(ESP8266) // Must enable this explicitly for ESP8266 Custom build
|
||||
#define FEATURE_MQTT_STATE_CLASS 0
|
||||
#else
|
||||
#define FEATURE_MQTT_STATE_CLASS 1
|
||||
#endif
|
||||
#endif
|
||||
#if !FEATURE_MQTT_DISCOVER && FEATURE_MQTT_STATE_CLASS
|
||||
#undef FEATURE_MQTT_STATE_CLASS
|
||||
#define FEATURE_MQTT_STATE_CLASS 0
|
||||
#endif
|
||||
|
||||
#ifndef FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
#if defined(LIMIT_BUILD_SIZE) || defined(ESP8266) // Must enable this explicitly for ESP8266 Custom build
|
||||
#define FEATURE_CUSTOM_TASKVAR_VTYPE 0
|
||||
|
||||
@@ -324,6 +324,21 @@ uint8_t Caches::getTaskVarCustomVType(taskIndex_t TaskIndex,
|
||||
|
||||
#endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
|
||||
#if FEATURE_MQTT_STATE_CLASS
|
||||
uint8_t Caches::getTaskVarStateClass(taskIndex_t TaskIndex,
|
||||
taskVarIndex_t taskVarIndex) {
|
||||
if (validTaskIndex(TaskIndex) && (validTaskVarIndex(taskVarIndex))) {
|
||||
auto it = getExtraTaskSettings(TaskIndex);
|
||||
|
||||
if (it != extraTaskSettings_cache.end()) {
|
||||
return it->second.mqttStateClass[taskVarIndex];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // if FEATURE_MQTT_STATE_CLASS
|
||||
|
||||
void Caches::updateExtraTaskSettingsCache()
|
||||
{
|
||||
const taskIndex_t TaskIndex = ExtraTaskSettings.TaskIndex;
|
||||
@@ -374,6 +389,9 @@ void Caches::updateExtraTaskSettingsCache()
|
||||
#if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
tmp.customVType[i] = ExtraTaskSettings.getTaskVarCustomVType(i);
|
||||
#endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
#if FEATURE_MQTT_STATE_CLASS
|
||||
tmp.mqttStateClass[i] = ExtraTaskSettings.getTaskVarStateClass(i);
|
||||
#endif // if FEATURE_MQTT_STATE_CLASS
|
||||
}
|
||||
#ifdef ESP32
|
||||
tmp.TaskDevicePluginConfigLong_index_used = 0;
|
||||
|
||||
@@ -50,13 +50,16 @@ struct ExtraTaskSettings_cache_t {
|
||||
#if FEATURE_PLUGIN_STATS
|
||||
PluginStats_Config_t pluginStatsConfig[VARS_PER_TASK] = {};
|
||||
#endif // if FEATURE_PLUGIN_STATS
|
||||
uint8_t hasFormula = 0; // Bitmap which task value has formula and whether a formula needs previous value
|
||||
uint8_t hasFormula = 0; // Bitmap which task value has formula and whether a formula needs previous value
|
||||
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
|
||||
uint8_t unitOfMeasure[VARS_PER_TASK] = { 0 }; // Value for unit of measure per taskValue
|
||||
uint8_t unitOfMeasure[VARS_PER_TASK] = { 0 }; // Value for unit of measure per taskValue
|
||||
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
|
||||
#if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
uint8_t customVType[VARS_PER_TASK] = { 0 }; // single-value VType per taskValue
|
||||
uint8_t customVType[VARS_PER_TASK] = { 0 }; // single-value VType per taskValue
|
||||
#endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
#if FEATURE_MQTT_STATE_CLASS
|
||||
uint8_t mqttStateClass[VARS_PER_TASK] = { 0 }; // state_class = None, Measurement, Measurement Angle, Total, Total Increasing
|
||||
#endif // if FEATURE_MQTT_STATE_CLASS
|
||||
};
|
||||
|
||||
typedef std::map<String, taskIndex_t> TaskIndexNameMap;
|
||||
@@ -91,9 +94,11 @@ struct Caches {
|
||||
uint8_t rel_index);
|
||||
|
||||
// Check to see if at least one of the taskvalues has a non-empty formula field.
|
||||
bool hasFormula(taskIndex_t TaskIndex, uint8_t rel_index);
|
||||
bool hasFormula(taskIndex_t TaskIndex,
|
||||
uint8_t rel_index);
|
||||
bool hasFormula(taskIndex_t TaskIndex);
|
||||
bool hasFormula_with_prevValue(taskIndex_t TaskIndex, uint8_t rel_index);
|
||||
bool hasFormula_with_prevValue(taskIndex_t TaskIndex,
|
||||
uint8_t rel_index);
|
||||
|
||||
|
||||
String getTaskDeviceFormula(taskIndex_t TaskIndex,
|
||||
@@ -116,14 +121,19 @@ struct Caches {
|
||||
|
||||
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
|
||||
uint8_t getTaskVarUnitOfMeasure(taskIndex_t taskIndex,
|
||||
taskVarIndex_t taskVarIndex);
|
||||
taskVarIndex_t taskVarIndex);
|
||||
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
|
||||
|
||||
|
||||
#if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
uint8_t getTaskVarCustomVType(taskIndex_t taskIndex,
|
||||
taskVarIndex_t taskVarIndex);
|
||||
#endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
|
||||
|
||||
#if FEATURE_MQTT_STATE_CLASS
|
||||
uint8_t getTaskVarStateClass(taskIndex_t taskIndex,
|
||||
taskVarIndex_t taskVarIndex);
|
||||
#endif // if FEATURE_MQTT_STATE_CLASS
|
||||
|
||||
// Update all cached values, except the checksum.
|
||||
void updateExtraTaskSettingsCache();
|
||||
|
||||
@@ -152,7 +162,7 @@ public:
|
||||
|
||||
TaskIndexNameMap taskIndexName;
|
||||
TaskIndexValueNameMap taskIndexValueName;
|
||||
FilePresenceMap fileExistsMap; // Filesize. -1 if not present
|
||||
FilePresenceMap fileExistsMap; // Filesize. -1 if not present
|
||||
RulesHelperClass rulesHelper;
|
||||
|
||||
private:
|
||||
|
||||
@@ -120,8 +120,8 @@ struct DeviceStruct
|
||||
uint32_t I2CNoBusSelection : 1; // Dis-allow I2C Bus selection in device configuration
|
||||
|
||||
uint32_t CustomVTypeVar : 1; // User-selectable VType per value
|
||||
uint32_t Dummy22 : 1; // Dummy added to force alignment, can be re-used
|
||||
uint32_t SpiBusSelect : 1; // Allow selection of the SPI bus, if multiple buses are configured
|
||||
uint32_t MqttStateClass : 1; // MQTT StateClass setting in DevicesPage
|
||||
uint32_t Dummy24 : 1; // Dummy added to force alignment, can be re-used
|
||||
uint32_t Dummy25 : 1; // Dummy added to force alignment, can be re-used
|
||||
uint32_t Dummy26 : 1; // Dummy added to force alignment, can be re-used
|
||||
|
||||
@@ -290,6 +290,20 @@ void ExtraTaskSettingsStruct::setTaskVarCustomVType(taskVarIndex_t taskVarIndex,
|
||||
}
|
||||
#endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
|
||||
#if FEATURE_MQTT_STATE_CLASS
|
||||
uint8_t ExtraTaskSettingsStruct::getTaskVarStateClass(taskVarIndex_t taskVarIndex) const {
|
||||
if (!validTaskVarIndex(taskVarIndex)) { return 0u; }
|
||||
return get3BitFromUL(VariousBits[taskVarIndex], 24); // 3 Bits 24, 25, 26
|
||||
}
|
||||
|
||||
void ExtraTaskSettingsStruct::setTaskVarStateClass(taskVarIndex_t taskVarIndex,
|
||||
uint8_t stateClass) {
|
||||
if (validTaskVarIndex(taskVarIndex)) {
|
||||
set3BitToUL(VariousBits[taskVarIndex], 24, stateClass); // 3 Bits 24, 25, 26
|
||||
}
|
||||
}
|
||||
#endif // if FEATURE_MQTT_STATE_CLASS
|
||||
|
||||
|
||||
void ExtraTaskSettingsStruct::populateDeviceValueNamesSeq(
|
||||
const __FlashStringHelper *valuename,
|
||||
|
||||
@@ -84,6 +84,12 @@ struct ExtraTaskSettingsStruct
|
||||
uint8_t customVType);
|
||||
#endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
|
||||
#if FEATURE_MQTT_STATE_CLASS
|
||||
uint8_t getTaskVarStateClass(taskVarIndex_t taskVarIndex) const;
|
||||
void setTaskVarStateClass(taskVarIndex_t taskVarIndex,
|
||||
uint8_t stateClass);
|
||||
#endif // if FEATURE_MQTT_STATE_CLASS
|
||||
|
||||
void populateDeviceValueNamesSeq(const __FlashStringHelper *valuename,
|
||||
size_t nrValues,
|
||||
uint8_t defaultDecimals,
|
||||
|
||||
@@ -264,6 +264,11 @@ class SettingsStruct_tmpl
|
||||
bool isPriorityTask(taskIndex_t taskIndex) const;
|
||||
#endif // if FEATURE_PLUGIN_PRIORITY
|
||||
|
||||
#if FEATURE_MQTT
|
||||
bool SendRetainedTaskValues(taskIndex_t taskIndex, controllerIndex_t controllerIndex) const;
|
||||
void SendRetainedTaskValues(taskIndex_t taskIndex, controllerIndex_t controllerIndex, bool value);
|
||||
#endif // if FEATURE_MQTT
|
||||
|
||||
void validate();
|
||||
|
||||
bool networkSettingsEmpty() const;
|
||||
|
||||
@@ -479,6 +479,23 @@ bool SettingsStruct_tmpl<N_TASKS>::isPriorityTask(taskIndex_t taskIndex) const {
|
||||
}
|
||||
#endif // if FEATURE_PLUGIN_PRIORITY
|
||||
|
||||
#if FEATURE_MQTT
|
||||
template<unsigned int N_TASKS>
|
||||
bool SettingsStruct_tmpl<N_TASKS>::SendRetainedTaskValues(taskIndex_t taskIndex, controllerIndex_t controllerIndex) const {
|
||||
if (validTaskIndex(taskIndex) && validControllerIndex(controllerIndex)) {
|
||||
return bitRead(VariousTaskBits[taskIndex], 2 + controllerIndex); // ATTENTION: uses bits 2..5!!!
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<unsigned int N_TASKS>
|
||||
void SettingsStruct_tmpl<N_TASKS>::SendRetainedTaskValues(taskIndex_t taskIndex, controllerIndex_t controllerIndex, bool value) {
|
||||
if (validTaskIndex(taskIndex) && validControllerIndex(controllerIndex)) {
|
||||
bitWrite(VariousTaskBits[taskIndex], 2 + controllerIndex, value); // ATTENTION: uses bits 2..5!!!
|
||||
}
|
||||
}
|
||||
#endif // if FEATURE_MQTT
|
||||
|
||||
template<unsigned int N_TASKS>
|
||||
ExtTimeSource_e SettingsStruct_tmpl<N_TASKS>::ExtTimeSource() const {
|
||||
return static_cast<ExtTimeSource_e>(ExternalTimeSource >> 1);
|
||||
|
||||
@@ -97,6 +97,26 @@ uint8_t getValueCountFromSensorType(Sensor_VType sensorType, bool log)
|
||||
case Sensor_VType::SENSOR_TYPE_DATA_SIZE:
|
||||
case Sensor_VType::SENSOR_TYPE_SOUND_PRESSURE:
|
||||
case Sensor_VType::SENSOR_TYPE_SIGNAL_STRENGTH:
|
||||
case Sensor_VType::SENSOR_TYPE_REACTIVE_ENERGY:
|
||||
case Sensor_VType::SENSOR_TYPE_FREQUENCY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY_STORAGE:
|
||||
case Sensor_VType::SENSOR_TYPE_ABS_HUMIDITY:
|
||||
case Sensor_VType::SENSOR_TYPE_ATMOS_PRESSURE:
|
||||
case Sensor_VType::SENSOR_TYPE_BLOOD_GLUCOSE_C:
|
||||
case Sensor_VType::SENSOR_TYPE_CO_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY_DISTANCE:
|
||||
case Sensor_VType::SENSOR_TYPE_GAS_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_NITROUS_OXIDE:
|
||||
case Sensor_VType::SENSOR_TYPE_OZONE_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_PRECIPITATION:
|
||||
case Sensor_VType::SENSOR_TYPE_PRECIPITATION_INTEN:
|
||||
case Sensor_VType::SENSOR_TYPE_SULPHUR_DIOXIDE:
|
||||
case Sensor_VType::SENSOR_TYPE_VOC_PARTS:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME_FLOW_RATE:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME_STORAGE:
|
||||
case Sensor_VType::SENSOR_TYPE_WATER:
|
||||
return 1;
|
||||
}
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
@@ -183,6 +203,26 @@ const __FlashStringHelper* getSensorTypeLabel(Sensor_VType sensorType) {
|
||||
case Sensor_VType::SENSOR_TYPE_DATA_SIZE: return F("Data size");
|
||||
case Sensor_VType::SENSOR_TYPE_SOUND_PRESSURE: return F("Sound pressure");
|
||||
case Sensor_VType::SENSOR_TYPE_SIGNAL_STRENGTH: return F("Signal strength");
|
||||
case Sensor_VType::SENSOR_TYPE_REACTIVE_ENERGY: return F("Reactive Energy");
|
||||
case Sensor_VType::SENSOR_TYPE_FREQUENCY: return F("Frequency");
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY: return F("Energy");
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY_STORAGE: return F("Energy storage");
|
||||
case Sensor_VType::SENSOR_TYPE_ABS_HUMIDITY: return F("Absolute humidity");
|
||||
case Sensor_VType::SENSOR_TYPE_ATMOS_PRESSURE: return F("Atmospheric pressure");
|
||||
case Sensor_VType::SENSOR_TYPE_BLOOD_GLUCOSE_C: return F("Blood glucose conc.");
|
||||
case Sensor_VType::SENSOR_TYPE_CO_ONLY: return F("CO");
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY_DISTANCE: return F("Energy distance");
|
||||
case Sensor_VType::SENSOR_TYPE_GAS_ONLY: return F("Gas");
|
||||
case Sensor_VType::SENSOR_TYPE_NITROUS_OXIDE: return F("N2O");
|
||||
case Sensor_VType::SENSOR_TYPE_OZONE_ONLY: return F("Ozone");
|
||||
case Sensor_VType::SENSOR_TYPE_PRECIPITATION: return F("Precipitation");
|
||||
case Sensor_VType::SENSOR_TYPE_PRECIPITATION_INTEN: return F("Precipitation intensity");
|
||||
case Sensor_VType::SENSOR_TYPE_SULPHUR_DIOXIDE: return F("SO2");
|
||||
case Sensor_VType::SENSOR_TYPE_VOC_PARTS: return F("VOC parts");
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME: return F("Volume");
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME_FLOW_RATE: return F("Volume flow rate");
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME_STORAGE: return F("Volume storage");
|
||||
case Sensor_VType::SENSOR_TYPE_WATER: return F("Water cons.");
|
||||
#else // if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
case Sensor_VType::SENSOR_TYPE_ANALOG_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_TEMP_ONLY:
|
||||
@@ -223,6 +263,26 @@ const __FlashStringHelper* getSensorTypeLabel(Sensor_VType sensorType) {
|
||||
case Sensor_VType::SENSOR_TYPE_DATA_SIZE:
|
||||
case Sensor_VType::SENSOR_TYPE_SOUND_PRESSURE:
|
||||
case Sensor_VType::SENSOR_TYPE_SIGNAL_STRENGTH:
|
||||
case Sensor_VType::SENSOR_TYPE_REACTIVE_ENERGY:
|
||||
case Sensor_VType::SENSOR_TYPE_FREQUENCY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY_STORAGE:
|
||||
case Sensor_VType::SENSOR_TYPE_ABS_HUMIDITY:
|
||||
case Sensor_VType::SENSOR_TYPE_ATMOS_PRESSURE:
|
||||
case Sensor_VType::SENSOR_TYPE_BLOOD_GLUCOSE_C:
|
||||
case Sensor_VType::SENSOR_TYPE_CO_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY_DISTANCE:
|
||||
case Sensor_VType::SENSOR_TYPE_GAS_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_NITROUS_OXIDE:
|
||||
case Sensor_VType::SENSOR_TYPE_OZONE_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_PRECIPITATION:
|
||||
case Sensor_VType::SENSOR_TYPE_PRECIPITATION_INTEN:
|
||||
case Sensor_VType::SENSOR_TYPE_SULPHUR_DIOXIDE:
|
||||
case Sensor_VType::SENSOR_TYPE_VOC_PARTS:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME_FLOW_RATE:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME_STORAGE:
|
||||
case Sensor_VType::SENSOR_TYPE_WATER:
|
||||
break;
|
||||
#endif // if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
}
|
||||
@@ -312,6 +372,26 @@ bool isMQTTDiscoverySensorType(Sensor_VType sensorType)
|
||||
case Sensor_VType::SENSOR_TYPE_DATA_SIZE:
|
||||
case Sensor_VType::SENSOR_TYPE_SOUND_PRESSURE:
|
||||
case Sensor_VType::SENSOR_TYPE_SIGNAL_STRENGTH:
|
||||
case Sensor_VType::SENSOR_TYPE_REACTIVE_ENERGY:
|
||||
case Sensor_VType::SENSOR_TYPE_FREQUENCY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY_STORAGE:
|
||||
case Sensor_VType::SENSOR_TYPE_ABS_HUMIDITY:
|
||||
case Sensor_VType::SENSOR_TYPE_ATMOS_PRESSURE:
|
||||
case Sensor_VType::SENSOR_TYPE_BLOOD_GLUCOSE_C:
|
||||
case Sensor_VType::SENSOR_TYPE_CO_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY_DISTANCE:
|
||||
case Sensor_VType::SENSOR_TYPE_GAS_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_NITROUS_OXIDE:
|
||||
case Sensor_VType::SENSOR_TYPE_OZONE_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_PRECIPITATION:
|
||||
case Sensor_VType::SENSOR_TYPE_PRECIPITATION_INTEN:
|
||||
case Sensor_VType::SENSOR_TYPE_SULPHUR_DIOXIDE:
|
||||
case Sensor_VType::SENSOR_TYPE_VOC_PARTS:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME_FLOW_RATE:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME_STORAGE:
|
||||
case Sensor_VType::SENSOR_TYPE_WATER:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -410,21 +490,26 @@ const char mqtt_valueType_ha_deviceclass_names[] PROGMEM = // !! Offset, startin
|
||||
"irradiance|irradiance|irradiance|mdi:scale|" // UV_ONLY .. WEIGHT_ONLY
|
||||
"voltage|current|power|power_factor|power|" // VOLTAGE_ONLY .. APPRNT_POWER_USG_ONLY
|
||||
"volatile_organic_compounds|pressure|mdi:palette|mdi:palette|mdi:palette|" // TVOC_ONLY .. COLOR_BLUE_ONLY
|
||||
"mdi:temperature-kelvin|power|aqi|nitrogen_dioxide|" // COLOR_TEMP_ONLY .. NOX_ONLY
|
||||
"mdi:temperature-kelvin|reactive_power|aqi|nitrogen_dioxide|" // COLOR_TEMP_ONLY .. NOX_ONLY
|
||||
"|wind_speed|duration|date|" // SWITCH_INVERTED .. DATE
|
||||
"timestamp|data_rate|data_size|sound_pressure|signal_strength|datetime|" // TIMESTAMP .. DATETIME
|
||||
"timestamp|data_rate|data_size|sound_pressure|signal_strength|reactive_energy|" // TIMESTAMP .. REACTIVE_ENERGY
|
||||
"frequency|energy|energy_storage|" // FREQUENCY .. ENERGY_STORAGE
|
||||
"absolute_humidity|atmospheric_pressure|blood_glucose_concentration|" // ABS_HUMIDITY .. ATMOSPH_PRESSURE
|
||||
"carbon_monoxide|energy_distance|gas|nitrous_oxide|ozone|" // CO_ONLY .. OZONE_ONLY
|
||||
"precipitation|precipitation_intensity|sulphur_dioxide|volatile_organic_compounds_parts|" // PRECIPITATION .. VOC_PARTS
|
||||
"volume|volume_flow_rate|volume_storage|water|" // VOLUME .. WATER
|
||||
;
|
||||
|
||||
/**
|
||||
* getValueType2HADeviceClass: Convert the ValueType to a HA Device Class
|
||||
*/
|
||||
String getValueType2HADeviceClass(Sensor_VType sensorType) {
|
||||
char tmp[27]{}; // length: volatile_organic_compounds + \0
|
||||
char tmp[33]{}; // length: volatile_organic_compounds_parts + \0
|
||||
int devClassIndex = static_cast<int>(sensorType);
|
||||
if (sensorType >= Sensor_VType::SENSOR_TYPE_ANALOG_ONLY) {
|
||||
devClassIndex -= static_cast<int>(Sensor_VType::SENSOR_TYPE_ANALOG_ONLY); // Subtract offset
|
||||
// } else if (sensorType == Sensor_VType::SENSOR_TYPE_SWITCH) {
|
||||
// return F("");
|
||||
// return EMPTY_STRING;
|
||||
} else if (sensorType == Sensor_VType::SENSOR_TYPE_WIND) {
|
||||
return F("wind_speed");
|
||||
// } else if (sensorType == Sensor_VType::SENSOR_TYPE_DIMMER) {
|
||||
@@ -442,11 +527,16 @@ const char mqtt_valueType_default_ha_uom_names[] PROGMEM = // !! Offset, startin
|
||||
"|°C|%|lx|cm|°|" // ANALOG_ONLY .. DIRECTION_ONLY
|
||||
"µg/m³|µg/m³|µg/m³|%|ppm||" // DUSTPM2_5_ONLY .. GPS_ONLY
|
||||
"W/m²|UV Index|W/m²|kg|" // UV_ONLY .. WEIGHT_ONLY
|
||||
"V|A|kWh|Cos φ|VA|" // VOLTAGE_ONLY .. APPRNT_POWER_USG_ONLY
|
||||
"V|A|W|%|VA|" // VOLTAGE_ONLY .. APPRNT_POWER_USG_ONLY
|
||||
"ppd|hPa|lx|lx|lx|" // TVOC_ONLY .. COLOR_BLUE_ONLY
|
||||
"K|var||µg/m³|" // COLOR_TEMP_ONLY .. NOX_ONLY
|
||||
"|m/s|min||" // SWITCH_INVERTED .. DATE
|
||||
"|bit/s|B|dB|dBm||" // TIMESTAMP .. DATETIME
|
||||
"|bit/s|B|dB|dBm|kvar|" // TIMESTAMP .. REACTIVE_ENERGY
|
||||
"Hz|kWh|kWh|" // FREQUENCY .. ENERGY_STORAGE
|
||||
"g/m³|mbar|mmol/L|" // ABS_HUMIDITY .. BLOOD_GLUCOSE_C
|
||||
"ppm|kWh/100km|L|µg/m³|µg/m³|" // CO_ONLY .. OZONE_ONLY
|
||||
"mm|mm/h|µg/m³|ppm|" // PRICIPIATION .. VOC_PARTS
|
||||
"L|m³/h|L|L|" // VOLUME .. WATER
|
||||
;
|
||||
|
||||
/**
|
||||
@@ -462,7 +552,7 @@ String getValueType2DefaultHAUoM(Sensor_VType sensorType) {
|
||||
} else if (sensorType == Sensor_VType::SENSOR_TYPE_WIND) {
|
||||
return F("m/s");
|
||||
// } else if (sensorType == Sensor_VType::SENSOR_TYPE_DIMMER) {
|
||||
// return F("");
|
||||
// return EMPTY_STRING;
|
||||
} else {
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,26 @@ enum class Sensor_VType : uint8_t {
|
||||
SENSOR_TYPE_DATA_SIZE = 136,
|
||||
SENSOR_TYPE_SOUND_PRESSURE = 137,
|
||||
SENSOR_TYPE_SIGNAL_STRENGTH = 138,
|
||||
SENSOR_TYPE_REACTIVE_ENERGY = 139,
|
||||
SENSOR_TYPE_FREQUENCY = 140,
|
||||
SENSOR_TYPE_ENERGY = 141,
|
||||
SENSOR_TYPE_ENERGY_STORAGE = 142,
|
||||
SENSOR_TYPE_ABS_HUMIDITY = 143,
|
||||
SENSOR_TYPE_ATMOS_PRESSURE = 144,
|
||||
SENSOR_TYPE_BLOOD_GLUCOSE_C = 145,
|
||||
SENSOR_TYPE_CO_ONLY = 146,
|
||||
SENSOR_TYPE_ENERGY_DISTANCE = 147,
|
||||
SENSOR_TYPE_GAS_ONLY = 148,
|
||||
SENSOR_TYPE_NITROUS_OXIDE = 149,
|
||||
SENSOR_TYPE_OZONE_ONLY = 150,
|
||||
SENSOR_TYPE_PRECIPITATION = 151,
|
||||
SENSOR_TYPE_PRECIPITATION_INTEN = 152,
|
||||
SENSOR_TYPE_SULPHUR_DIOXIDE = 153,
|
||||
SENSOR_TYPE_VOC_PARTS = 154,
|
||||
SENSOR_TYPE_VOLUME = 155,
|
||||
SENSOR_TYPE_VOLUME_FLOW_RATE = 156,
|
||||
SENSOR_TYPE_VOLUME_STORAGE = 157,
|
||||
SENSOR_TYPE_WATER = 158,
|
||||
|
||||
SENSOR_TYPE_NOT_SET = 255
|
||||
};
|
||||
|
||||
@@ -448,6 +448,21 @@ bool MQTT_binary_deviceClassTwoWay(int devClassIndex) {
|
||||
|
||||
# endif // if FEATURE_MQTT_DEVICECLASS
|
||||
|
||||
# if FEATURE_MQTT_STATE_CLASS
|
||||
const __FlashStringHelper* MQTT_sensor_StateClass(uint8_t index,
|
||||
bool display) {
|
||||
switch (index) {
|
||||
case 0: return F("");
|
||||
case 1: return display ? F("Measurement") : F("measurement");
|
||||
case 2: return display ? F("Total") : F("total");
|
||||
case 3: return display ? F("Total-increasing") : F("total_increasing");
|
||||
case 4: return display ? F("Measurement-angle") : F("measurement_angle");
|
||||
}
|
||||
return F("");
|
||||
}
|
||||
|
||||
# endif // if FEATURE_MQTT_STATE_CLASS
|
||||
|
||||
bool MQTT_SendAutoDiscovery(controllerIndex_t ControllerIndex, cpluginID_t CPluginID) {
|
||||
bool success = true;
|
||||
|
||||
@@ -521,7 +536,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("MQTT : Start AutoDiscovery for task %d, %s max. %d value%c"),
|
||||
x + 1, taskName.c_str(), valueCount, 1 == valueCount ? 's' : ' '));
|
||||
x + 1, taskName.c_str(), valueCount, 1 != valueCount ? 's' : ' '));
|
||||
}
|
||||
|
||||
String pluginDeviceClass;
|
||||
@@ -536,7 +551,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
const bool useGroupId = groupId != 0 && !usesControllerIDX;
|
||||
|
||||
const String elementName = useGroupId ?
|
||||
strformat(F("Group %u"), groupId) :
|
||||
(Settings.Unit == groupId ? hostName : strformat(F("Group %u"), groupId)) :
|
||||
strformat(F("%s %s"), hostName.c_str(), taskName.c_str());
|
||||
const String elementIds = useGroupId ?
|
||||
strformat(F("group_%u"), groupId) :
|
||||
@@ -616,6 +631,11 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
String valueDeviceClass = parseStringKeepCase(pluginDeviceClass, v + 1); // Device classes per value
|
||||
|
||||
if (valueDeviceClass.isEmpty()) { valueDeviceClass = F("power"); } // default
|
||||
# if FEATURE_MQTT_STATE_CLASS
|
||||
const String stateClass = MQTT_sensor_StateClass(Cache.getTaskVarStateClass(x, v), false);
|
||||
# else // if FEATURE_MQTT_STATE_CLASS
|
||||
const String stateClass = EMPTY_STRING;
|
||||
# endif // if FEATURE_MQTT_STATE_CLASS
|
||||
# if FEATURE_MQTT_DEVICECLASS
|
||||
const bool twoWay = MQTT_binary_deviceClassTwoWay(MQTT_binary_deviceClassIndex(valueDeviceClass));
|
||||
# else // if FEATURE_MQTT_DEVICECLASS
|
||||
@@ -637,6 +657,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
EMPTY_STRING, // No unit of measure used
|
||||
&TempEvent,
|
||||
deviceElement,
|
||||
EMPTY_STRING,
|
||||
success,
|
||||
true, false,
|
||||
useGroupId ? elementName : EMPTY_STRING, elementIds,
|
||||
@@ -650,6 +671,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
uom,
|
||||
&TempEvent,
|
||||
deviceElement,
|
||||
stateClass,
|
||||
success,
|
||||
discoveryItems[s].canSet, false,
|
||||
useGroupId ? elementName : EMPTY_STRING, elementIds);
|
||||
@@ -680,6 +702,11 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
const String valuename = MQTT_DiscoveryHelperGetValueName(x, v, discoveryItems[s]);
|
||||
const String uom = MQTT_DiscoveryHelperGetValueUoM(x, v, discoveryItems[s],
|
||||
getValueType2DefaultHAUoM(Sensor_VType::SENSOR_TYPE_TEMP_ONLY));
|
||||
# if FEATURE_MQTT_STATE_CLASS
|
||||
const String stateClass = MQTT_sensor_StateClass(Cache.getTaskVarStateClass(x, v), false);
|
||||
# else // if FEATURE_MQTT_STATE_CLASS
|
||||
const String stateClass = EMPTY_STRING;
|
||||
# endif // if FEATURE_MQTT_STATE_CLASS
|
||||
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, valuename,
|
||||
ControllerIndex,
|
||||
ControllerSettings,
|
||||
@@ -688,6 +715,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
uom,
|
||||
&TempEvent,
|
||||
deviceElement,
|
||||
stateClass,
|
||||
success,
|
||||
discoveryItems[s].canSet, false,
|
||||
useGroupId ? elementName : EMPTY_STRING, elementIds);
|
||||
@@ -709,6 +737,11 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
const String valuename = MQTT_DiscoveryHelperGetValueName(x, v, discoveryItems[s]);
|
||||
const String uom = MQTT_DiscoveryHelperGetValueUoM(x, v, discoveryItems[s],
|
||||
getValueType2DefaultHAUoM(Sensor_VType::SENSOR_TYPE_HUM_ONLY));
|
||||
# if FEATURE_MQTT_STATE_CLASS
|
||||
const String stateClass = MQTT_sensor_StateClass(Cache.getTaskVarStateClass(x, v), false);
|
||||
# else // if FEATURE_MQTT_STATE_CLASS
|
||||
const String stateClass = EMPTY_STRING;
|
||||
# endif // if FEATURE_MQTT_STATE_CLASS
|
||||
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, valuename,
|
||||
ControllerIndex,
|
||||
ControllerSettings,
|
||||
@@ -717,6 +750,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
uom,
|
||||
&TempEvent,
|
||||
deviceElement,
|
||||
stateClass,
|
||||
success,
|
||||
discoveryItems[s].canSet, false,
|
||||
useGroupId ? elementName : EMPTY_STRING, elementIds);
|
||||
@@ -742,6 +776,11 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
const String valuename = MQTT_DiscoveryHelperGetValueName(x, v, discoveryItems[s]);
|
||||
const String uom = MQTT_DiscoveryHelperGetValueUoM(x, v, discoveryItems[s],
|
||||
getValueType2DefaultHAUoM(Sensor_VType::SENSOR_TYPE_BARO_ONLY));
|
||||
# if FEATURE_MQTT_STATE_CLASS
|
||||
const String stateClass = MQTT_sensor_StateClass(Cache.getTaskVarStateClass(x, v), false);
|
||||
# else // if FEATURE_MQTT_STATE_CLASS
|
||||
const String stateClass = EMPTY_STRING;
|
||||
# endif // if FEATURE_MQTT_STATE_CLASS
|
||||
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, valuename,
|
||||
ControllerIndex,
|
||||
ControllerSettings,
|
||||
@@ -750,6 +789,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
uom,
|
||||
&TempEvent,
|
||||
deviceElement,
|
||||
stateClass,
|
||||
success,
|
||||
discoveryItems[s].canSet, false,
|
||||
useGroupId ? elementName : EMPTY_STRING, elementIds);
|
||||
@@ -792,6 +832,26 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
case Sensor_VType::SENSOR_TYPE_DATA_SIZE:
|
||||
case Sensor_VType::SENSOR_TYPE_SOUND_PRESSURE:
|
||||
case Sensor_VType::SENSOR_TYPE_SIGNAL_STRENGTH:
|
||||
case Sensor_VType::SENSOR_TYPE_REACTIVE_ENERGY:
|
||||
case Sensor_VType::SENSOR_TYPE_FREQUENCY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY_STORAGE:
|
||||
case Sensor_VType::SENSOR_TYPE_ABS_HUMIDITY:
|
||||
case Sensor_VType::SENSOR_TYPE_ATMOS_PRESSURE:
|
||||
case Sensor_VType::SENSOR_TYPE_BLOOD_GLUCOSE_C:
|
||||
case Sensor_VType::SENSOR_TYPE_CO_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_ENERGY_DISTANCE:
|
||||
case Sensor_VType::SENSOR_TYPE_GAS_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_NITROUS_OXIDE:
|
||||
case Sensor_VType::SENSOR_TYPE_OZONE_ONLY:
|
||||
case Sensor_VType::SENSOR_TYPE_PRECIPITATION:
|
||||
case Sensor_VType::SENSOR_TYPE_PRECIPITATION_INTEN:
|
||||
case Sensor_VType::SENSOR_TYPE_SULPHUR_DIOXIDE:
|
||||
case Sensor_VType::SENSOR_TYPE_VOC_PARTS:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME_FLOW_RATE:
|
||||
case Sensor_VType::SENSOR_TYPE_VOLUME_STORAGE:
|
||||
case Sensor_VType::SENSOR_TYPE_WATER:
|
||||
{
|
||||
const String dev = getValueType2HADeviceClass(discoveryItems[s].VType);
|
||||
const String uomDef = getValueType2DefaultHAUoM(discoveryItems[s].VType);
|
||||
@@ -799,6 +859,11 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
|
||||
const String valuename = MQTT_DiscoveryHelperGetValueName(x, v, discoveryItems[s]);
|
||||
const String uom = MQTT_DiscoveryHelperGetValueUoM(x, v, discoveryItems[s], uomDef);
|
||||
# if FEATURE_MQTT_STATE_CLASS
|
||||
const String stateClass = MQTT_sensor_StateClass(Cache.getTaskVarStateClass(x, v), false);
|
||||
# else // if FEATURE_MQTT_STATE_CLASS
|
||||
const String stateClass = EMPTY_STRING;
|
||||
# endif // if FEATURE_MQTT_STATE_CLASS
|
||||
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, valuename,
|
||||
ControllerIndex,
|
||||
ControllerSettings,
|
||||
@@ -807,6 +872,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
|
||||
uom,
|
||||
&TempEvent,
|
||||
deviceElement,
|
||||
stateClass,
|
||||
success,
|
||||
discoveryItems[s].canSet, false,
|
||||
useGroupId ? elementName : EMPTY_STRING, elementIds);
|
||||
@@ -1009,6 +1075,7 @@ bool MQTT_DiscoveryPublishWithStatusAndSet(taskIndex_t taskIndex,
|
||||
String unitOfMeasure,
|
||||
struct EventStruct *event,
|
||||
const String deviceElement,
|
||||
const String stateClass,
|
||||
bool success,
|
||||
bool hasSet,
|
||||
bool hasIcon,
|
||||
@@ -1019,9 +1086,10 @@ bool MQTT_DiscoveryPublishWithStatusAndSet(taskIndex_t taskIndex,
|
||||
const String withSet = hasSet ? F(",\"cmd_t\":\"~/set\"") : EMPTY_STRING;
|
||||
const String schema = hasSet ? EMPTY_STRING : "\"schema\":\"basic\",";
|
||||
const String devOrIcon = hasIcon ? F("ic") : F("dev_cla");
|
||||
const String withUoM = !unitOfMeasure.isEmpty() ?
|
||||
strformat(F(",\"unit_of_meas\":\"%s\""), unitOfMeasure.c_str()) :
|
||||
EMPTY_STRING;
|
||||
const String withUoM = unitOfMeasure.isEmpty() ? EMPTY_STRING :
|
||||
strformat(F(",\"unit_of_meas\":\"%s\""), unitOfMeasure.c_str());
|
||||
const String stateJson = stateClass.isEmpty() ? EMPTY_STRING :
|
||||
strformat(F(",\"stat_cla\":\"%s\""), stateClass.c_str());
|
||||
const String taskName = makeHomeAssistantCompliantName(getTaskDeviceName(taskIndex));
|
||||
const String valName = makeHomeAssistantCompliantName(valueName);
|
||||
const bool retainDsc = ControllerSettings.mqtt_retainDiscovery();
|
||||
@@ -1046,10 +1114,10 @@ bool MQTT_DiscoveryPublishWithStatusAndSet(taskIndex_t taskIndex,
|
||||
elementId,
|
||||
valName);
|
||||
const String discoveryMessage = strformat(F("{\"~\":\"%s\",\"name\":\"%s %s\",\"uniq_id\":\"%s\",%s"
|
||||
"\"%s\":\"%s\"%s%s,\"stat_t\":\"~\""
|
||||
"\"%s\":\"%s\"%s%s%s,\"stat_t\":\"~\""
|
||||
"%s}"), // deviceElement last
|
||||
publish.c_str(), taskName.c_str(), valName.c_str(), uniqueId.c_str(), schema.c_str(),
|
||||
devOrIcon.c_str(), deviceClass.c_str(), withUoM.c_str(), withSet.c_str(),
|
||||
devOrIcon.c_str(), deviceClass.c_str(), withUoM.c_str(), stateJson.c_str(), withSet.c_str(),
|
||||
deviceElement.c_str());
|
||||
const String triggerMessage = strformat(F("{\"atype\":\"trigger\",\"t\":\"%s\","
|
||||
"\"p\":\"device_automation\","
|
||||
|
||||
@@ -23,23 +23,27 @@ bool getDiscoveryVType(struct EventStruct *event,
|
||||
uint8_t pConfigOffset,
|
||||
uint8_t nrVars);
|
||||
|
||||
int Plugin_QueryVType_BinarySensor(uint8_t value_nr);
|
||||
int Plugin_QueryVType_BinarySensorInv(uint8_t value_nr);
|
||||
int Plugin_QueryVType_Analog(uint8_t value_nr);
|
||||
int Plugin_QueryVType_CO2(uint8_t value_nr);
|
||||
int Plugin_QueryVType_Distance(uint8_t value_nr);
|
||||
int Plugin_QueryVType_DustPM2_5(uint8_t value_nr);
|
||||
int Plugin_QueryVType_Lux(uint8_t value_nr);
|
||||
int Plugin_QueryVType_Temperature(uint8_t value_nr);
|
||||
int Plugin_QueryVType_Weight(uint8_t value_nr);
|
||||
int Plugin_QueryVType_BinarySensor(uint8_t value_nr);
|
||||
int Plugin_QueryVType_BinarySensorInv(uint8_t value_nr);
|
||||
int Plugin_QueryVType_Analog(uint8_t value_nr);
|
||||
int Plugin_QueryVType_CO2(uint8_t value_nr);
|
||||
int Plugin_QueryVType_Distance(uint8_t value_nr);
|
||||
int Plugin_QueryVType_DustPM2_5(uint8_t value_nr);
|
||||
int Plugin_QueryVType_Lux(uint8_t value_nr);
|
||||
int Plugin_QueryVType_Temperature(uint8_t value_nr);
|
||||
int Plugin_QueryVType_Weight(uint8_t value_nr);
|
||||
|
||||
String makeHomeAssistantCompliantName(const String& name);
|
||||
String makeHomeAssistantCompliantName(const String& name);
|
||||
|
||||
# if FEATURE_MQTT_DEVICECLASS
|
||||
String MQTT_binary_deviceClassName(int devClassIndex);
|
||||
bool MQTT_binary_deviceClassTwoWay(int devClassIndex);
|
||||
int MQTT_binary_deviceClassIndex(const String& deviceClassName);
|
||||
String MQTT_binary_deviceClassName(int devClassIndex);
|
||||
bool MQTT_binary_deviceClassTwoWay(int devClassIndex);
|
||||
int MQTT_binary_deviceClassIndex(const String& deviceClassName);
|
||||
# endif // if FEATURE_MQTT_DEVICECLASS
|
||||
# if FEATURE_MQTT_STATE_CLASS
|
||||
const __FlashStringHelper* MQTT_sensor_StateClass(uint8_t index,
|
||||
bool display = true);
|
||||
# endif // if FEATURE_MQTT_STATE_CLASS
|
||||
struct DiscoveryItem {
|
||||
DiscoveryItem(Sensor_VType _VType, int _valueCount, taskVarIndex_t _varIndex, const bool _canSet = false)
|
||||
: VType(_VType), valueCount(_valueCount), varIndex(_varIndex), canSet(_canSet) {}
|
||||
@@ -96,6 +100,7 @@ bool MQTT_DiscoveryPublishWithStatusAndSet(taskIndex_t taskIndex,
|
||||
String unitOfMeasure,
|
||||
struct EventStruct *event,
|
||||
const String deviceElement,
|
||||
const String stateClass,
|
||||
bool success,
|
||||
bool hasSet,
|
||||
bool hasIcon,
|
||||
|
||||
@@ -97,15 +97,16 @@ String toValueTypeCategory(const uint32_t valueType) {
|
||||
/* *INDENT-OFF* */
|
||||
|
||||
const uint16_t value_type_map[] PROGMEM = {
|
||||
0, // None
|
||||
1024, 1, 5, 6, 7, // Basic
|
||||
1025, 2, 3, 4, 8, 101, 102, 122, 21, 131, // Environment
|
||||
1026, 106, 107, 108, 110, 121, 128, 129, // Gases
|
||||
1027, 116, 117, 118, 119, 120, 127, // Energy
|
||||
1028, 132, 133, 134, // Time
|
||||
1029, 100, 104, 105, 109, 111, 115, 135, 136, 137, 138, // Size
|
||||
1030, 103, 112, 113, 114, 123, 124, 125, 126, // Light
|
||||
1031, 10, 130, 11, 22, 20, 31, 32, 33, 40, 41, 42, 43, 50, 51, 60, 61, 70, 71, // Other
|
||||
0, // None
|
||||
1024, 1, 5, 6, 7, // Basic
|
||||
1025, 2, 3, 4, 8, 101, 102, 122, 21, 131, 143, 144, 151, 152, // Environment
|
||||
1026, 106, 107, 108, 110, 146, 121, 154, 128, 129, 148, 149, 150, 153, // Gases
|
||||
1027, 116, 117, 118, 119, 120, 127, 139, 141, 142, 147, // Energy
|
||||
1028, 132, 133, 134, // Time
|
||||
1029, 100, 104, 105, 109, 111, 115, 135, 136, 137, 138, 155, 156, 157, 158, // Size
|
||||
1030, 103, 112, 113, 114, 123, 124, 125, 126, // Light
|
||||
1031, 10, 130, 11, 22, 20, 31, 32, 33, 40, 41, 42, 43, 50, 51, 60, 61, 70, 71, 140, // Other
|
||||
145, // Other
|
||||
};
|
||||
|
||||
/* *INDENT-ON* */
|
||||
|
||||
@@ -371,11 +371,7 @@ constexpr /*pluginID_t*/ uint8_t DeviceIndex_to_Plugin_id[] PROGMEM =
|
||||
#endif // ifdef USES_P088
|
||||
|
||||
#ifdef USES_P089
|
||||
# ifdef ESP8266
|
||||
|
||||
// FIXME TD-er: Support Ping plugin for ESP32
|
||||
89,
|
||||
# endif // ifdef ESP8266
|
||||
#endif // ifdef USES_P089
|
||||
|
||||
#ifdef USES_P090
|
||||
@@ -1404,11 +1400,7 @@ constexpr const Plugin_ptr_t PROGMEM Plugin_ptr[] =
|
||||
#endif // ifdef USES_P088
|
||||
|
||||
#ifdef USES_P089
|
||||
# ifdef ESP8266
|
||||
|
||||
// FIXME TD-er: Support Ping plugin for ESP32
|
||||
&Plugin_089,
|
||||
# endif // ifdef ESP8266
|
||||
#endif // ifdef USES_P089
|
||||
|
||||
#ifdef USES_P090
|
||||
|
||||
@@ -390,10 +390,7 @@ void PluginInit(bool priorityOnly = false);
|
||||
#endif
|
||||
|
||||
#ifdef USES_P089
|
||||
#ifdef ESP8266
|
||||
// FIXME TD-er: Support Ping plugin for ESP32
|
||||
ADDPLUGIN_H(089)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USES_P090
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
#include "../PluginStructs/P056_data_struct.h"
|
||||
|
||||
#ifdef USES_P056
|
||||
|
||||
P056_data_struct::P056_data_struct(EventStruct *event) {
|
||||
delete SDS;
|
||||
|
||||
const ESPEasySerialPort port = static_cast<ESPEasySerialPort>(CONFIG_PORT);
|
||||
SDS = new (std::nothrow) CjkSDS011(port, CONFIG_PIN1, CONFIG_PIN2);
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("SDS : Init OK ESP GPIO-pin RX:%d TX:%d"), CONFIG_PIN1, CONFIG_PIN2));
|
||||
}
|
||||
}
|
||||
|
||||
P056_data_struct::~P056_data_struct() {
|
||||
delete SDS;
|
||||
SDS = nullptr;
|
||||
}
|
||||
|
||||
void P056_data_struct::Process() {
|
||||
if (isInitialized()) {
|
||||
this->SDS->Process();
|
||||
}
|
||||
}
|
||||
|
||||
bool P056_data_struct::available() {
|
||||
if (isInitialized()) {
|
||||
return this->SDS->available();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
float P056_data_struct::GetPM2_5() {
|
||||
if (isInitialized()) {
|
||||
return this->SDS->GetPM2_5();
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float P056_data_struct::GetPM10_() {
|
||||
if (isInitialized()) {
|
||||
return this->SDS->GetPM10_();
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool P056_data_struct::ReadAverage(float pm25, float pm10) {
|
||||
if (isInitialized()) {
|
||||
return this->SDS->ReadAverage(pm25, pm10);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void P056_data_struct::SetWorkingPeriod(int minutes) {
|
||||
if (isInitialized()) {
|
||||
this->SDS->SetWorkingPeriod(minutes);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../_Plugin_Helper.h"
|
||||
#ifdef USES_P056
|
||||
|
||||
# include <jkSDS011.h>
|
||||
|
||||
struct P056_data_struct : public PluginTaskData_base {
|
||||
P056_data_struct(EventStruct *event);
|
||||
~P056_data_struct();
|
||||
|
||||
bool isInitialized() {
|
||||
return nullptr != SDS;
|
||||
}
|
||||
|
||||
void Process();
|
||||
bool available();
|
||||
float GetPM2_5();
|
||||
float GetPM10_();
|
||||
bool ReadAverage(float pm25,
|
||||
float pm10);
|
||||
void SetWorkingPeriod(int minutes);
|
||||
|
||||
private:
|
||||
|
||||
CjkSDS011 *SDS = nullptr;
|
||||
};
|
||||
#endif // ifdef USES_P056
|
||||
@@ -33,7 +33,7 @@ int Plugin_077_QueryVType(uint8_t value_nr) {
|
||||
Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY,
|
||||
Sensor_VType::SENSOR_TYPE_CURRENT_ONLY,
|
||||
Sensor_VType::SENSOR_TYPE_NONE,
|
||||
Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY,
|
||||
Sensor_VType::SENSOR_TYPE_ENERGY,
|
||||
Sensor_VType::SENSOR_TYPE_APPRNT_POWER_USG_ONLY,
|
||||
Sensor_VType::SENSOR_TYPE_POWER_FACT_ONLY,
|
||||
Sensor_VType::SENSOR_TYPE_REACTIVE_POWER_ONLY,
|
||||
|
||||
@@ -38,12 +38,12 @@ int Plugin_085_QueryVType(uint8_t value_nr) {
|
||||
case P085_QUERY_V: result = Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY; break;
|
||||
case P085_QUERY_A: result = Sensor_VType::SENSOR_TYPE_CURRENT_ONLY; break;
|
||||
case P085_QUERY_W: result = Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY; break;
|
||||
case P085_QUERY_Wh_imp: // return Sensor_VType::SENSOR_TYPE_NONE; // FIXME
|
||||
case P085_QUERY_Wh_exp: // return Sensor_VType::SENSOR_TYPE_NONE; // FIXME
|
||||
case P085_QUERY_Wh_tot: // return Sensor_VType::SENSOR_TYPE_NONE; // FIXME
|
||||
case P085_QUERY_Wh_net: // return Sensor_VType::SENSOR_TYPE_NONE; // FIXME
|
||||
case P085_QUERY_h_tot: // return Sensor_VType::SENSOR_TYPE_NONE; // FIXME
|
||||
case P085_QUERY_h_load: result = Sensor_VType::SENSOR_TYPE_NONE; break; // FIXME
|
||||
case P085_QUERY_Wh_imp:
|
||||
case P085_QUERY_Wh_exp:
|
||||
case P085_QUERY_Wh_tot: // Fall through
|
||||
case P085_QUERY_Wh_net: result = Sensor_VType::SENSOR_TYPE_ENERGY; break;
|
||||
case P085_QUERY_h_tot: // Fall through
|
||||
case P085_QUERY_h_load: result = Sensor_VType::SENSOR_TYPE_DURATION; break;
|
||||
}
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,114 @@
|
||||
#include "../PluginStructs/P089_data_struct.h"
|
||||
|
||||
#if defined(USES_P089) && defined(ESP8266)
|
||||
#ifdef USES_P089
|
||||
|
||||
|
||||
#include "../Helpers/Networking.h"
|
||||
# include "../Helpers/Networking.h"
|
||||
|
||||
#include "../Helpers/_Plugin_init.h"
|
||||
# include "../Helpers/_Plugin_init.h"
|
||||
|
||||
# ifdef ESP32
|
||||
P089_data_struct::P089_data_struct() {
|
||||
if (nullptr == P089_ping_service) {
|
||||
P089_ping_service = new (std::nothrow) P089_ping_service_struct();
|
||||
addLog(LOG_LEVEL_INFO, F("PING : Starting ping service."));
|
||||
}
|
||||
|
||||
if ((nullptr != P089_ping_service) && P089_ping_service->isInitialized()) {
|
||||
addLog(LOG_LEVEL_INFO, F("PING : Increment task instance counter."));
|
||||
P089_ping_service->increment();
|
||||
}
|
||||
}
|
||||
|
||||
P089_data_struct::~P089_data_struct() {
|
||||
if (nullptr != P089_ping_service) {
|
||||
addLog(LOG_LEVEL_INFO, F("PING : Decrement task instance counter."));
|
||||
|
||||
if (0 == P089_ping_service->decrement()) {
|
||||
addLog(LOG_LEVEL_INFO, F("PING : Stopping ping service."));
|
||||
delete P089_ping_service;
|
||||
P089_ping_service = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool P089_data_struct::send_ping(struct EventStruct *event) {
|
||||
/* This ping lost for sure */
|
||||
if (!isInitialized() || !NetworkConnected()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((nullptr == P089_ping_service) ||
|
||||
!P089_ping_service->isInitialized()) {
|
||||
return true; // Service not available
|
||||
}
|
||||
|
||||
if (P089_ping_service->getPingResult(event->TaskIndex, _ping_request)) {
|
||||
// Get requested result
|
||||
|
||||
if (_ping_request.status == P089_request_status::Result) {
|
||||
if (_ping_request.result) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("PING : Successfully pinged %s (%s) count: %d avg: %.03f ms for task: %d"),
|
||||
_ping_request.hostname.c_str(),
|
||||
_ping_request.ip.toString().c_str(),
|
||||
_ping_request.count,
|
||||
_ping_request.avgTime,
|
||||
event->TaskIndex + 1));
|
||||
} else {
|
||||
addLog(LOG_LEVEL_ERROR, strformat(F("PING : Error pinging %s (%s) for task: %d"),
|
||||
_ping_request.hostname.c_str(),
|
||||
_ping_request.ip.toString().c_str(),
|
||||
event->TaskIndex + 1));
|
||||
}
|
||||
UserVar.setFloat(event->TaskIndex, 1, _ping_request.avgTime); // Set always, even when not specifically enabled
|
||||
|
||||
_ping_request.status = P089_request_status::Finished; // Ready for new work
|
||||
return !_ping_request.result; // Inverted result to report back!
|
||||
}
|
||||
}
|
||||
|
||||
if ((_ping_request.status > P089_request_status::Request) && (_ping_request.status < P089_request_status::Result)) {
|
||||
// addLog(LOG_LEVEL_INFO, F("PING : Still working."));
|
||||
return false; // Busy, but not a failure
|
||||
}
|
||||
|
||||
// New request
|
||||
|
||||
IPAddress ip;
|
||||
char hostname[PLUGIN_089_HOSTNAME_SIZE]{};
|
||||
|
||||
LoadCustomTaskSettings(event->TaskIndex, (uint8_t *)&hostname, PLUGIN_089_HOSTNAME_SIZE);
|
||||
|
||||
/* This one lost as well, DNS dead? */
|
||||
if (!resolveHostByName(hostname, ip)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int16_t pingCount = P089_PING_COUNT;
|
||||
|
||||
if ((pingCount < 1) || (pingCount > P089_MAX_PING_COUNT)) {
|
||||
pingCount = 5;
|
||||
}
|
||||
|
||||
_ping_request.status = P089_request_status::Request;
|
||||
_ping_request.count = pingCount;
|
||||
_ping_request.ip = ip;
|
||||
_ping_request.hostname = hostname; // Bring all data to the request
|
||||
const bool result = !P089_ping_service->addPingRequest(event->TaskIndex, _ping_request);
|
||||
P089_ping_service->loop(); // Kick-off if not already working
|
||||
return result;
|
||||
}
|
||||
|
||||
bool P089_data_struct::loop() {
|
||||
if ((nullptr != P089_ping_service) && P089_ping_service->isInitialized()) {
|
||||
return P089_ping_service->loop();
|
||||
}
|
||||
return false; // We did nothing
|
||||
}
|
||||
|
||||
# endif // ifdef ESP32
|
||||
|
||||
# ifdef ESP8266
|
||||
P089_data_struct::P089_data_struct() {
|
||||
destIPAddress.addr = 0;
|
||||
idseq = 0;
|
||||
@@ -159,4 +260,5 @@ uint8_t PingReceiver(void *origin, struct raw_pcb *pcb, struct pbuf *packetBuffe
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // if defined(USES_P089) && defined(ESP8266)
|
||||
# endif // ifdef ESP8266
|
||||
#endif // ifdef USES_P089
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#define PLUGINSTRUCTS_P089_DATA_STRUCT_H
|
||||
|
||||
#include "../../_Plugin_Helper.h"
|
||||
#if defined(USES_P089) && defined(ESP8266)
|
||||
#ifdef USES_P089
|
||||
|
||||
# include "src/ESPEasyCore/ESPEasyNetwork.h"
|
||||
|
||||
|
||||
# define PLUGIN_ID_089 89
|
||||
@@ -12,17 +14,46 @@
|
||||
|
||||
# define ICMP_PAYLOAD_LEN 32
|
||||
|
||||
# ifdef ESP32
|
||||
|
||||
# include "../PluginStructs/P089_ping_service_struct.h"
|
||||
|
||||
# define P089_PING_COUNT PCONFIG(0)
|
||||
# define P089_VALUE_COUNT PCONFIG(1)
|
||||
|
||||
# define P089_MAX_PING_COUNT 25
|
||||
|
||||
struct P089_data_struct : public PluginTaskData_base {
|
||||
public:
|
||||
|
||||
P089_data_struct();
|
||||
|
||||
virtual ~P089_data_struct();
|
||||
|
||||
bool isInitialized() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool send_ping(struct EventStruct *event);
|
||||
bool loop();
|
||||
|
||||
private:
|
||||
|
||||
P089_ping_request _ping_request;
|
||||
};
|
||||
# endif // ifdef ESP32
|
||||
|
||||
# ifdef ESP8266
|
||||
|
||||
extern "C"
|
||||
{
|
||||
# include <lwip/raw.h>
|
||||
# include <lwip/icmp.h> // needed for icmp packet definitions
|
||||
# include <lwip/inet_chksum.h> // needed for inet_chksum()
|
||||
# include <lwip/sys.h> // needed for sys_now()
|
||||
# include <lwip/netif.h>
|
||||
# include <lwip/raw.h>
|
||||
# include <lwip/icmp.h> // needed for icmp packet definitions
|
||||
# include <lwip/inet_chksum.h> // needed for inet_chksum()
|
||||
# include <lwip/sys.h> // needed for sys_now()
|
||||
# include <lwip/netif.h>
|
||||
}
|
||||
|
||||
# include "src/ESPEasyCore/ESPEasyNetwork.h"
|
||||
|
||||
|
||||
struct P089_icmp_pcb {
|
||||
P089_icmp_pcb() = default;
|
||||
@@ -39,6 +70,10 @@ public:
|
||||
|
||||
bool send_ping(struct EventStruct *event);
|
||||
|
||||
bool isInitialized() const {
|
||||
return nullptr != P089_data;
|
||||
}
|
||||
|
||||
struct P089_icmp_pcb *P089_data = nullptr;
|
||||
ip_addr_t destIPAddress;
|
||||
uint32_t idseq;
|
||||
@@ -50,6 +85,6 @@ uint8_t PingReceiver(void *origin,
|
||||
struct pbuf *packetBuffer,
|
||||
const ip_addr_t *addr);
|
||||
|
||||
|
||||
#endif // if defined(USES_P089) && defined(ESP8266)
|
||||
# endif // ifdef ESP8266
|
||||
#endif // ifdef USES_P089
|
||||
#endif // ifndef PLUGINSTRUCTS_P089_DATA_STRUCT_H
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
|
||||
#include "../PluginStructs/P089_ping_service_struct.h"
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
P089_ping_service_struct*P089_ping_service = nullptr;
|
||||
|
||||
/**
|
||||
* Ping execute handler task
|
||||
*/
|
||||
void P089_execute_ping_task(void *parameter)
|
||||
{
|
||||
P089_ping_task_data*ping_task_data = static_cast<P089_ping_task_data *>(parameter);
|
||||
|
||||
if ((ping_task_data->status == P089_task_status::Working) && (nullptr != ping_task_data->espPing)) {
|
||||
// Blocking operation
|
||||
ping_task_data->result = ping_task_data->espPing->ping(ping_task_data->ip, ping_task_data->count);
|
||||
|
||||
// Results are in
|
||||
ping_task_data->avgTime = ping_task_data->espPing->averageTime();
|
||||
ping_task_data->status = P089_task_status::Ready;
|
||||
}
|
||||
|
||||
ping_task_data->taskHandle = NULL;
|
||||
vTaskDelete(ping_task_data->taskHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* P089_ping_service_struct
|
||||
*/
|
||||
P089_ping_service_struct::P089_ping_service_struct() {
|
||||
espPing = new (std::nothrow) PingClass();
|
||||
}
|
||||
|
||||
P089_ping_service_struct::~P089_ping_service_struct() {
|
||||
if (_ping_task_data.taskHandle) {
|
||||
vTaskDelete(_ping_task_data.taskHandle);
|
||||
_ping_task_data.taskHandle = NULL;
|
||||
}
|
||||
_requests.clear();
|
||||
delete espPing;
|
||||
espPing = nullptr;
|
||||
}
|
||||
|
||||
bool P089_ping_service_struct::addPingRequest(taskIndex_t TaskIndex,
|
||||
P089_ping_request request) {
|
||||
bool result = false; // Something is not ok
|
||||
auto it = _requests.find(TaskIndex);
|
||||
|
||||
if ((request.status == P089_request_status::Request) &&
|
||||
((it == _requests.end()) ||
|
||||
((it->first == TaskIndex) && (it->second.status == P089_request_status::Finished)))) { // No request or Finished for this task
|
||||
_requests[TaskIndex] = request;
|
||||
_requests[TaskIndex].status = P089_request_status::Accepted;
|
||||
_requests[TaskIndex].lastStarted = millis();
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool P089_ping_service_struct::getPingResult(taskIndex_t TaskIndex,
|
||||
P089_ping_request& request) {
|
||||
bool result = false;
|
||||
auto it = _requests.find(TaskIndex);
|
||||
|
||||
if (it != _requests.end()) {
|
||||
request = it->second;
|
||||
|
||||
if (it->second.status == P089_request_status::Result) {
|
||||
it->second.status = P089_request_status::Finished; // Can be removed
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we need to clean outdated requests
|
||||
it = _requests.begin();
|
||||
|
||||
while (it != _requests.end()) {
|
||||
// Cleanup when counter reached
|
||||
if (timePassedSince(it->second.lastStarted) >= P089_FINISHED_REQUEST_TIMEOUT_MS) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("PING : Clean last request for task %d (%u s)"),
|
||||
it->first + 1, timePassedSince(it->second.lastStarted) / 1000));
|
||||
_requests.erase(it); // Cleanup
|
||||
it = _requests.begin(); // Restart loop
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool P089_ping_service_struct::loop() {
|
||||
bool result = false;
|
||||
|
||||
if (timePassedSince(lastLooped) < 100) { // Some call/load throtteling
|
||||
return result;
|
||||
}
|
||||
lastLooped = millis();
|
||||
|
||||
// addLog(LOG_LEVEL_INFO, F("PING : Running P089_ping_service->loop()")); // FIXME
|
||||
|
||||
if (_ping_task_data.status == P089_task_status::Ready) {
|
||||
auto it = _requests.find(_ping_task_data.taskIndex);
|
||||
|
||||
if (it != _requests.end()) {
|
||||
it->second.result = _ping_task_data.result;
|
||||
it->second.avgTime = _ping_task_data.avgTime;
|
||||
it->second.status = P089_request_status::Result; // Can be retrieved
|
||||
result = true;
|
||||
}
|
||||
_ping_task_data.status = P089_task_status::Initial; // Available for new job
|
||||
}
|
||||
|
||||
if (_ping_task_data.status == P089_task_status::Initial) { // Find next job
|
||||
auto it = _requests.begin();
|
||||
|
||||
for (; it != _requests.end(); ++it) {
|
||||
if (it->second.status == P089_request_status::Accepted) {
|
||||
_ping_task_data.taskIndex = it->first;
|
||||
_ping_task_data.status = P089_task_status::Working;
|
||||
_ping_task_data.count = it->second.count;
|
||||
_ping_task_data.ip = it->second.ip;
|
||||
_ping_task_data.espPing = espPing;
|
||||
|
||||
xTaskCreatePinnedToCore(
|
||||
P089_execute_ping_task, // Function that should be called
|
||||
"PingClass.ping()", // Name of the task (for debugging)
|
||||
4000, // Stack size (bytes)
|
||||
&_ping_task_data, // Parameter to pass
|
||||
1, // Task priority
|
||||
&_ping_task_data.taskHandle, // Task handle
|
||||
xPortGetCoreID() // Core you want to run the task on (0 or 1)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // ifdef ESP32
|
||||
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../_Plugin_Helper.h"
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
# include <ESPping.h>
|
||||
# include <map>
|
||||
|
||||
constexpr uint32_t P089_FINISHED_REQUEST_TIMEOUT_MS = 1000 * 60 * 10; // Delete entry after unused for this number of milliseconds (10 min.)
|
||||
|
||||
struct P089_ping_service_struct; // Forward declaration
|
||||
|
||||
extern P089_ping_service_struct*P089_ping_service;
|
||||
|
||||
enum class P089_task_status : uint8_t {
|
||||
Initial = 0, // Ready to be start a task
|
||||
Working = 1, // Working on a task
|
||||
Ready = 2, // Ready, data is available
|
||||
};
|
||||
|
||||
enum class P089_request_status : uint8_t {
|
||||
Request = 0, // Initial request
|
||||
Accepted = 1, // Can be processed
|
||||
Processing = 2, // Actively being processed
|
||||
Ready = 3, // Results in
|
||||
Result = 4, // Results retrieved
|
||||
Finished = 5, // Can be removed or reused
|
||||
};
|
||||
|
||||
struct P089_ping_task_data {
|
||||
taskIndex_t taskIndex{};
|
||||
P089_task_status status = P089_task_status::Initial;
|
||||
IPAddress ip;
|
||||
bool result{};
|
||||
uint16_t count{};
|
||||
float avgTime{};
|
||||
PingClass *espPing = nullptr;
|
||||
|
||||
// This is C-code, so not set to nullptr, but to NULL
|
||||
TaskHandle_t taskHandle = NULL;
|
||||
};
|
||||
|
||||
struct P089_ping_request {
|
||||
P089_request_status status = P089_request_status::Request;
|
||||
IPAddress ip;
|
||||
String hostname;
|
||||
uint16_t count{};
|
||||
bool result{};
|
||||
float avgTime{};
|
||||
uint32_t lastStarted{};
|
||||
};
|
||||
|
||||
struct P089_ping_service_struct {
|
||||
public:
|
||||
|
||||
P089_ping_service_struct();
|
||||
virtual ~P089_ping_service_struct();
|
||||
|
||||
bool isInitialized() {
|
||||
return nullptr != espPing;
|
||||
}
|
||||
|
||||
bool addPingRequest(taskIndex_t TaskIndex,
|
||||
P089_ping_request request);
|
||||
bool getPingResult(taskIndex_t TaskIndex,
|
||||
P089_ping_request& request);
|
||||
bool loop();
|
||||
|
||||
uint8_t increment() {
|
||||
return ++taskCounter; // No checks needed?
|
||||
}
|
||||
|
||||
uint8_t decrement() {
|
||||
return --taskCounter; // No checks needed?
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uint32_t lastLooped{};
|
||||
uint8_t taskCounter{};
|
||||
|
||||
std::map<taskIndex_t, P089_ping_request>_requests;
|
||||
PingClass *espPing = nullptr;
|
||||
P089_ping_task_data _ping_task_data;
|
||||
};
|
||||
|
||||
#endif // ifdef ESP32
|
||||
@@ -39,10 +39,10 @@ int Plugin_108_QueryVType(uint8_t value_nr) {
|
||||
Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY,
|
||||
Sensor_VType::SENSOR_TYPE_REACTIVE_POWER_ONLY,
|
||||
Sensor_VType::SENSOR_TYPE_POWER_FACT_ONLY,
|
||||
Sensor_VType::SENSOR_TYPE_NONE, // FIXME To implement Frequency and Energy options
|
||||
Sensor_VType::SENSOR_TYPE_NONE,
|
||||
Sensor_VType::SENSOR_TYPE_NONE,
|
||||
Sensor_VType::SENSOR_TYPE_NONE,
|
||||
Sensor_VType::SENSOR_TYPE_FREQUENCY,
|
||||
Sensor_VType::SENSOR_TYPE_ENERGY,
|
||||
Sensor_VType::SENSOR_TYPE_ENERGY,
|
||||
Sensor_VType::SENSOR_TYPE_ENERGY,
|
||||
};
|
||||
constexpr uint8_t valueCount = NR_ELEMENTS(vtypes);
|
||||
Sensor_VType result = Sensor_VType::SENSOR_TYPE_NONE;
|
||||
|
||||
@@ -12,7 +12,15 @@ P168_data_struct::P168_data_struct(uint8_t alsGain,
|
||||
uint8_t psmMode,
|
||||
uint8_t readMethod) :
|
||||
_als_gain(alsGain), _als_integration(alsIntegration), _psm_mode(psmMode), _readMethod(readMethod), initialized(false)
|
||||
{}
|
||||
{
|
||||
// We already apply the correct WAIT for a read, so no need or desire to wait any longer.
|
||||
// AUTO enforces multiple WAIT options sequentially, so that's inhibited
|
||||
if (static_cast<uint8_t>(VEML_LUX_NORMAL) == _readMethod) {
|
||||
_readMethod = static_cast<uint8_t>(VEML_LUX_NORMAL_NOWAIT);
|
||||
} else if ((static_cast<uint8_t>(VEML_LUX_CORRECTED) == _readMethod) || (static_cast<uint8_t>(VEML_LUX_AUTO) == _readMethod)) {
|
||||
_readMethod = static_cast<uint8_t>(VEML_LUX_CORRECTED_NOWAIT);
|
||||
}
|
||||
}
|
||||
|
||||
P168_data_struct::~P168_data_struct() {
|
||||
delete veml;
|
||||
@@ -51,10 +59,10 @@ bool P168_data_struct::plugin_read(struct EventStruct *event) {
|
||||
float lux = veml->readLux(static_cast<luxMethod>(_readMethod));
|
||||
uint16_t whi = veml->readWhite();
|
||||
|
||||
if (luxMethod::VEML_LUX_AUTO == static_cast<luxMethod>(_readMethod)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("VEML : 6030/7700 AutoLux, Lux: %.2f, Gain: %.3f, Integration: %d"),
|
||||
lux, veml->getGainValue(), veml->getIntegrationTimeValue()));
|
||||
}
|
||||
// if (luxMethod::VEML_LUX_AUTO == static_cast<luxMethod>(_readMethod)) {
|
||||
// addLog(LOG_LEVEL_INFO, strformat(F("VEML : 6030/7700 AutoLux, Lux: %.2f, Gain: %.3f, Integration: %d"),
|
||||
// lux, veml->getGainValue(), veml->getIntegrationTimeValue()));
|
||||
// }
|
||||
UserVar.setFloat(event->TaskIndex, 0, lux);
|
||||
UserVar.setFloat(event->TaskIndex, 1, whi);
|
||||
UserVar.setFloat(event->TaskIndex, 2, amb);
|
||||
|
||||
@@ -415,6 +415,9 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task
|
||||
#if FEATURE_STRING_VARIABLES
|
||||
Settings.SendDerivedTaskValues(taskIndex, controllerNr, isFormItemChecked(getPluginCustomArgName(F("TSND"), controllerNr)));
|
||||
#endif // if FEATURE_STRING_VARIABLES
|
||||
#if FEATURE_MQTT && FEATURE_MQTT_DISCOVER
|
||||
Settings.SendRetainedTaskValues(taskIndex, controllerNr, isFormItemChecked(getPluginCustomArgName(F("TSRT"), controllerNr)));
|
||||
#endif // if FEATURE_MQTT && FEATURE_MQTT_DISCOVER
|
||||
}
|
||||
|
||||
if (device.PullUpOption) {
|
||||
@@ -464,6 +467,10 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task
|
||||
#if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
ExtraTaskSettings.setTaskVarCustomVType(varNr, getFormItemInt(getPluginCustomArgName(F("TDTV"), varNr)));
|
||||
#endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
|
||||
#if FEATURE_MQTT_STATE_CLASS
|
||||
ExtraTaskSettings.setTaskVarStateClass(varNr, getFormItemInt(getPluginCustomArgName(F("TDSC"), varNr)));
|
||||
#endif // if FEATURE_MQTT_STATE_CLASS
|
||||
}
|
||||
ExtraTaskSettings.clearUnusedValueNames(valueCount);
|
||||
|
||||
@@ -1636,6 +1643,18 @@ void devicePage_show_controller_config(taskIndex_t taskIndex, deviceIndex_t Devi
|
||||
getPluginCustomArgName(F("TDID"), controllerNr), // ="taskdeviceid"
|
||||
Settings.TaskDeviceID[controllerNr][taskIndex], 0, DOMOTICZ_MAX_IDX);
|
||||
}
|
||||
#if FEATURE_MQTT && FEATURE_MQTT_DISCOVER
|
||||
if (showMqttGroup) {
|
||||
html_TD();
|
||||
addHtml(F("Retained:"));
|
||||
html_TD();
|
||||
addCheckBox(getPluginCustomArgName(F("TSRT"), controllerNr), Settings.SendRetainedTaskValues(taskIndex, controllerNr), false
|
||||
# if FEATURE_TOOLTIPS
|
||||
, F("Send values with Retain flag")
|
||||
# endif // if FEATURE_TOOLTIPS
|
||||
);
|
||||
}
|
||||
#endif // if FEATURE_MQTT && FEATURE_MQTT_DISCOVER
|
||||
# if FEATURE_STRING_VARIABLES
|
||||
if (allowSendDerived) {
|
||||
html_TD();
|
||||
@@ -1743,6 +1762,13 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde
|
||||
}
|
||||
#endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
|
||||
#if FEATURE_MQTT_STATE_CLASS
|
||||
if (device.MqttStateClass) {
|
||||
html_table_header(F("MQTT State Class"), 350);
|
||||
++colCount;
|
||||
}
|
||||
#endif // if FEATURE_MQTT_STATE_CLASS
|
||||
|
||||
|
||||
// placeholder header
|
||||
html_table_header(F(""));
|
||||
@@ -1845,6 +1871,25 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde
|
||||
static_cast<Sensor_VType>(Cache.getTaskVarCustomVType(taskIndex, varNr)));
|
||||
}
|
||||
#endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
||||
|
||||
#if FEATURE_MQTT_STATE_CLASS
|
||||
if (device.MqttStateClass) {
|
||||
html_TD();
|
||||
const __FlashStringHelper *stateClasses[] = {
|
||||
MQTT_sensor_StateClass(0),
|
||||
MQTT_sensor_StateClass(1),
|
||||
MQTT_sensor_StateClass(4),
|
||||
MQTT_sensor_StateClass(2),
|
||||
MQTT_sensor_StateClass(3),
|
||||
};
|
||||
|
||||
constexpr size_t stateCount = NR_ELEMENTS(stateClasses);
|
||||
const FormSelectorOptions selectorSC(stateCount, stateClasses);
|
||||
selectorSC.addSelector(
|
||||
getPluginCustomArgName(F("TDSC"), varNr),
|
||||
Cache.getTaskVarStateClass(taskIndex, varNr));
|
||||
}
|
||||
#endif // if FEATURE_MQTT_STATE_CLASS
|
||||
}
|
||||
addFormSeparator(colCount);
|
||||
}
|
||||
|
||||
@@ -396,6 +396,8 @@ const char unit_of_measure_list[] PROGMEM = // *** DO NOT CHANGE ORDER, SAVED IN
|
||||
"mg/dL|mmol/L|" // 150..151
|
||||
"μSv|μSv/h|" // 152..153
|
||||
"m³/s|ft³/min|L/h|L/min|L/s|gal/min|mL/s|" // 154..160
|
||||
"g/m³|kWh/100km|Wh/km|mi/kWh|km/kWh|" // 161..165
|
||||
"in/d|mm/d" // 166 .. 167
|
||||
; // *** DO NOT CHANGE ORDER, SAVED IN TASK SETTINGS! ***
|
||||
|
||||
|
||||
@@ -435,12 +437,13 @@ const uint16_t unit_of_measure_map[] PROGMEM = {
|
||||
1054, 26, 27, 153, 154, 155, 156, 157, 158, 159, 160, // Volume flow rate
|
||||
1032, 39, 40, 41, 42, 43, 44, 45, 46, 47, // Duration
|
||||
1034, 14, 15, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, // Energy(-storage)
|
||||
1033, 162, 163, 164, 165, // Energy distance
|
||||
1050, 66, 67, 68, 69, 70, 71, 65, 106, 107, // Speed
|
||||
1056, 35, // (Wind) direction
|
||||
1038, 28, // Illuminance
|
||||
1039, 64, 112, // Irradiance
|
||||
1046, 152, 153, // Radiation
|
||||
1057, 29, 63, // Various units
|
||||
1057, 29, 63, 161, 166, 167, // Various units
|
||||
1035, 52, 53, 131, 132, // Frequency
|
||||
1043, 113, // Potential hydrogen
|
||||
1026, 56, 122, 123, 124, 125, 126, 127, 128, 129, 130, // Area
|
||||
|
||||