mirror of https://github.com/arendst/Tasmota.git
Merge remote-tracking branch 'Tasmota/development' into development
This commit is contained in:
commit
767f4d3d7b
|
@ -55,10 +55,13 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
|||
### Version 8.3.1.2
|
||||
|
||||
- Change IRremoteESP8266 library updated to v2.7.7
|
||||
- Change Adafruit_SGP30 library from v1.0.3 to v1.2.0 (#8519)
|
||||
- Change Energy JSON Total field from ``"Total":[33.736,11.717,16.978]`` to ``"Total":33.736,"TotalTariff":[11.717,16.978]``
|
||||
- Change Energy JSON ExportActive field from ``"ExportActive":[33.736,11.717,16.978]`` to ``"ExportActive":33.736,"ExportTariff":[11.717,16.978]``
|
||||
- Add command ``Rule0`` to change global rule parameters
|
||||
- Add commands ``LedPwmOn 0..255``, ``LedPwmOff 0..255`` and ``LedPwmMode1 0/1`` to control led brightness by George (#8491)
|
||||
- Add more functionality to ``Switchmode`` 11 and 12 (#8450)
|
||||
- Add wildcard pattern ``?`` for JSON matching in rules
|
||||
- Add support for VEML6075 UVA/UVB/UVINDEX Sensor by device111 (#8432)
|
||||
- Add support for VEML7700 Ambient light intensity Sensor by device111 (#8432)
|
||||
- Add Three Phase Export Active Energy to SDM630 driver
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
Thank you for opening an issue on an Adafruit Arduino library repository. To
|
||||
improve the speed of resolution please review the following guidelines and
|
||||
common troubleshooting steps below before creating the issue:
|
||||
|
||||
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
|
||||
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
|
||||
something isn't working as expected. In many cases the problem is a common issue
|
||||
that you will more quickly receive help from the forum community. GitHub issues
|
||||
are meant for known defects in the code. If you don't know if there is a defect
|
||||
in the code then start with troubleshooting on the forum first.
|
||||
|
||||
- **If following a tutorial or guide be sure you didn't miss a step.** Carefully
|
||||
check all of the steps and commands to run have been followed. Consult the
|
||||
forum if you're unsure or have questions about steps in a guide/tutorial.
|
||||
|
||||
- **For Arduino projects check these very common issues to ensure they don't apply**:
|
||||
|
||||
- For uploading sketches or communicating with the board make sure you're using
|
||||
a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes
|
||||
very hard to tell the difference between a data and charge cable! Try using the
|
||||
cable with other devices or swapping to another cable to confirm it is not
|
||||
the problem.
|
||||
|
||||
- **Be sure you are supplying adequate power to the board.** Check the specs of
|
||||
your board and plug in an external power supply. In many cases just
|
||||
plugging a board into your computer is not enough to power it and other
|
||||
peripherals.
|
||||
|
||||
- **Double check all soldering joints and connections.** Flakey connections
|
||||
cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints.
|
||||
|
||||
- **Ensure you are using an official Arduino or Adafruit board.** We can't
|
||||
guarantee a clone board will have the same functionality and work as expected
|
||||
with this code and don't support them.
|
||||
|
||||
If you're sure this issue is a defect in the code and checked the steps above
|
||||
please fill in the following fields to provide enough troubleshooting information.
|
||||
You may delete the guideline and text above to just leave the following details:
|
||||
|
||||
- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE**
|
||||
|
||||
- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO
|
||||
VERSION HERE**
|
||||
|
||||
- List the steps to reproduce the problem below (if possible attach a sketch or
|
||||
copy the sketch code in too): **LIST REPRO STEPS BELOW**
|
|
@ -1,26 +0,0 @@
|
|||
Thank you for creating a pull request to contribute to Adafruit's GitHub code!
|
||||
Before you open the request please review the following guidelines and tips to
|
||||
help it be more easily integrated:
|
||||
|
||||
- **Describe the scope of your change--i.e. what the change does and what parts
|
||||
of the code were modified.** This will help us understand any risks of integrating
|
||||
the code.
|
||||
|
||||
- **Describe any known limitations with your change.** For example if the change
|
||||
doesn't apply to a supported platform of the library please mention it.
|
||||
|
||||
- **Please run any tests or examples that can exercise your modified code.** We
|
||||
strive to not break users of the code and running tests/examples helps with this
|
||||
process.
|
||||
|
||||
Thank you again for contributing! We will try to test and integrate the change
|
||||
as soon as we can, but be aware we have many GitHub repositories to manage and
|
||||
can't immediately respond to every request. There is no need to bump or check in
|
||||
on a pull request (it will clutter the discussion of the request).
|
||||
|
||||
Also don't be worried if the request is closed or not integrated--sometimes the
|
||||
priorities of Adafruit's GitHub code (education, ease of use) might not match the
|
||||
priorities of the pull request. Don't fret, the open source community thrives on
|
||||
forks and GitHub makes it easy to keep your changes in a forked repo.
|
||||
|
||||
After reviewing the guidelines above you can delete this text from the pull request.
|
|
@ -1,4 +0,0 @@
|
|||
*~
|
||||
Doxyfile*
|
||||
doxygen_sqlite3.db
|
||||
html
|
|
@ -1,267 +0,0 @@
|
|||
/*!
|
||||
* @file Adafruit_SGP30.cpp
|
||||
*
|
||||
* @mainpage Adafruit SGP30 gas sensor driver
|
||||
*
|
||||
* @section intro_sec Introduction
|
||||
*
|
||||
* This is the documentation for Adafruit's SGP30 driver for the
|
||||
* Arduino platform. It is designed specifically to work with the
|
||||
* Adafruit SGP30 breakout: http://www.adafruit.com/products/3709
|
||||
*
|
||||
* These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
|
||||
* to interface with the breakout.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing
|
||||
* products from Adafruit!
|
||||
*
|
||||
*
|
||||
* @section author Author
|
||||
* Written by Ladyada for Adafruit Industries.
|
||||
*
|
||||
* @section license License
|
||||
* BSD license, all text here must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#if ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include "Adafruit_SGP30.h"
|
||||
|
||||
//#define I2C_DEBUG
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Instantiates a new SGP30 class
|
||||
*/
|
||||
/**************************************************************************/
|
||||
Adafruit_SGP30::Adafruit_SGP30() {
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Setups the hardware and detects a valid SGP30. Initializes I2C
|
||||
then reads the serialnumber and checks that we are talking to an SGP30
|
||||
@param theWire Optional pointer to I2C interface, otherwise use Wire
|
||||
@returns True if SGP30 found on I2C, False if something went wrong!
|
||||
*/
|
||||
/**************************************************************************/
|
||||
boolean Adafruit_SGP30::begin(TwoWire *theWire) {
|
||||
_i2caddr = SGP30_I2CADDR_DEFAULT;
|
||||
if (theWire == NULL) {
|
||||
_i2c = &Wire;
|
||||
} else {
|
||||
_i2c = theWire;
|
||||
}
|
||||
|
||||
// assume i2c initialized already to avoid resetting clock stretching
|
||||
// _i2c->begin();
|
||||
|
||||
|
||||
uint8_t command[2];
|
||||
command[0] = 0x36;
|
||||
command[1] = 0x82;
|
||||
if (! readWordFromCommand(command, 2, 10, serialnumber, 3))
|
||||
return false;
|
||||
|
||||
uint16_t featureset;
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x2F;
|
||||
if (! readWordFromCommand(command, 2, 10, &featureset, 1))
|
||||
return false;
|
||||
//Serial.print("Featureset 0x"); Serial.println(featureset, HEX);
|
||||
if (featureset != SGP30_FEATURESET)
|
||||
return false;
|
||||
if (! IAQinit())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Commands the sensor to begin the IAQ algorithm. Must be called after startup.
|
||||
@returns True if command completed successfully, false if something went wrong!
|
||||
*/
|
||||
/**************************************************************************/
|
||||
boolean Adafruit_SGP30::IAQinit(void) {
|
||||
uint8_t command[2];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x03;
|
||||
return readWordFromCommand(command, 2, 10);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Commands the sensor to take a single eCO2/VOC measurement. Places results in {@link TVOC} and {@link eCO2}
|
||||
@returns True if command completed successfully, false if something went wrong!
|
||||
*/
|
||||
/**************************************************************************/
|
||||
boolean Adafruit_SGP30::IAQmeasure(void) {
|
||||
uint8_t command[2];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x08;
|
||||
uint16_t reply[2];
|
||||
if (! readWordFromCommand(command, 2, 12, reply, 2))
|
||||
return false;
|
||||
TVOC = reply[1];
|
||||
eCO2 = reply[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Request baseline calibration values for both CO2 and TVOC IAQ calculations. Places results in parameter memory locaitons.
|
||||
@param eco2_base A pointer to a uint16_t which we will save the calibration value to
|
||||
@param tvoc_base A pointer to a uint16_t which we will save the calibration value to
|
||||
@returns True if command completed successfully, false if something went wrong!
|
||||
*/
|
||||
/**************************************************************************/
|
||||
boolean Adafruit_SGP30::getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base) {
|
||||
uint8_t command[2];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x15;
|
||||
uint16_t reply[2];
|
||||
if (! readWordFromCommand(command, 2, 10, reply, 2))
|
||||
return false;
|
||||
*eco2_base = reply[0];
|
||||
*tvoc_base = reply[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Assign baseline calibration values for both CO2 and TVOC IAQ calculations.
|
||||
@param eco2_base A uint16_t which we will save the calibration value from
|
||||
@param tvoc_base A uint16_t which we will save the calibration value from
|
||||
@returns True if command completed successfully, false if something went wrong!
|
||||
*/
|
||||
/**************************************************************************/
|
||||
boolean Adafruit_SGP30::setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base) {
|
||||
uint8_t command[8];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x1e;
|
||||
command[2] = tvoc_base >> 8;
|
||||
command[3] = tvoc_base & 0xFF;
|
||||
command[4] = generateCRC(command+2, 2);
|
||||
command[5] = eco2_base >> 8;
|
||||
command[6] = eco2_base & 0xFF;
|
||||
command[7] = generateCRC(command+5, 2);
|
||||
|
||||
return readWordFromCommand(command, 8, 10);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Set the absolute humidity value [mg/m^3] for compensation to increase precision of TVOC and eCO2.
|
||||
@param absolute_humidity A uint32_t [mg/m^3] which we will be used for compensation. If the absolute humidity is set to zero, humidity compensation will be disabled.
|
||||
@returns True if command completed successfully, false if something went wrong!
|
||||
*/
|
||||
/**************************************************************************/
|
||||
boolean Adafruit_SGP30::setHumidity(uint32_t absolute_humidity) {
|
||||
if (absolute_humidity > 256000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t ah_scaled = (uint16_t)(((uint64_t)absolute_humidity * 256 * 16777) >> 24);
|
||||
uint8_t command[5];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x61;
|
||||
command[2] = ah_scaled >> 8;
|
||||
command[3] = ah_scaled & 0xFF;
|
||||
command[4] = generateCRC(command+2, 2);
|
||||
|
||||
return readWordFromCommand(command, 5, 10);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief I2C low level interfacing
|
||||
*/
|
||||
/**************************************************************************/
|
||||
|
||||
|
||||
boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[], uint8_t commandLength, uint16_t delayms, uint16_t *readdata, uint8_t readlen)
|
||||
{
|
||||
uint8_t data;
|
||||
|
||||
_i2c->beginTransmission(_i2caddr);
|
||||
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("\t\t-> ");
|
||||
#endif
|
||||
|
||||
for (uint8_t i=0; i<commandLength; i++) {
|
||||
_i2c->write(command[i]);
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("0x"); Serial.print(command[i], HEX); Serial.print(", ");
|
||||
#endif
|
||||
}
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.println();
|
||||
#endif
|
||||
_i2c->endTransmission();
|
||||
|
||||
delay(delayms);
|
||||
|
||||
if (readlen == 0)
|
||||
return true;
|
||||
|
||||
uint8_t replylen = readlen * (SGP30_WORD_LEN +1);
|
||||
if (_i2c->requestFrom(_i2caddr, replylen) != replylen)
|
||||
return false;
|
||||
uint8_t replybuffer[replylen];
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("\t\t<- ");
|
||||
#endif
|
||||
for (uint8_t i=0; i<replylen; i++) {
|
||||
replybuffer[i] = _i2c->read();
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("0x"); Serial.print(replybuffer[i], HEX); Serial.print(", ");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
for (uint8_t i=0; i<readlen; i++) {
|
||||
uint8_t crc = generateCRC(replybuffer+i*3, 2);
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("\t\tCRC calced: 0x"); Serial.print(crc, HEX);
|
||||
Serial.print(" vs. 0x"); Serial.println(replybuffer[i * 3 + 2], HEX);
|
||||
#endif
|
||||
if (crc != replybuffer[i * 3 + 2])
|
||||
return false;
|
||||
// success! store it
|
||||
readdata[i] = replybuffer[i*3];
|
||||
readdata[i] <<= 8;
|
||||
readdata[i] |= replybuffer[i*3 + 1];
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("\t\tRead: 0x"); Serial.println(readdata[i], HEX);
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t Adafruit_SGP30::generateCRC(uint8_t *data, uint8_t datalen) {
|
||||
// calculates 8-Bit checksum with given polynomial
|
||||
uint8_t crc = SGP30_CRC8_INIT;
|
||||
|
||||
for (uint8_t i=0; i<datalen; i++) {
|
||||
crc ^= data[i];
|
||||
for (uint8_t b=0; b<8; b++) {
|
||||
if (crc & 0x80)
|
||||
crc = (crc << 1) ^ SGP30_CRC8_POLYNOMIAL;
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*!
|
||||
* @file Adafruit_SGP30.h
|
||||
*
|
||||
* This is the documentation for Adafruit's SGP30 driver for the
|
||||
* Arduino platform. It is designed specifically to work with the
|
||||
* Adafruit SGP30 breakout: http://www.adafruit.com/products/3709
|
||||
*
|
||||
* These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
|
||||
* to interface with the breakout.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing
|
||||
* products from Adafruit!
|
||||
*
|
||||
* Written by Ladyada for Adafruit Industries.
|
||||
*
|
||||
* BSD license, all text here must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <Wire.h>
|
||||
|
||||
// the i2c address
|
||||
#define SGP30_I2CADDR_DEFAULT 0x58 ///< SGP30 has only one I2C address
|
||||
|
||||
// commands and constants
|
||||
#define SGP30_FEATURESET 0x0020 ///< The required set for this library
|
||||
#define SGP30_CRC8_POLYNOMIAL 0x31 ///< Seed for SGP30's CRC polynomial
|
||||
#define SGP30_CRC8_INIT 0xFF ///< Init value for CRC
|
||||
#define SGP30_WORD_LEN 2 ///< 2 bytes per word
|
||||
|
||||
/**************************************************************************/
|
||||
/*! Class that stores state and functions for interacting with SGP30 Gas Sensor */
|
||||
/**************************************************************************/
|
||||
class Adafruit_SGP30 {
|
||||
public:
|
||||
Adafruit_SGP30();
|
||||
boolean begin(TwoWire *theWire = NULL);
|
||||
boolean IAQinit(void);
|
||||
boolean IAQmeasure(void);
|
||||
|
||||
boolean getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base);
|
||||
boolean setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base);
|
||||
boolean setHumidity(uint32_t absolute_humidity);
|
||||
|
||||
/**
|
||||
* The last measurement of the IAQ-calculated Total Volatile Organic Compounds in ppb. This value is set when you call {@link IAQmeasure()}
|
||||
*/
|
||||
uint16_t TVOC;
|
||||
|
||||
/**
|
||||
* The last measurement of the IAQ-calculated equivalent CO2 in ppm. This value is set when you call {@link IAQmeasure()}
|
||||
*/
|
||||
uint16_t eCO2;
|
||||
|
||||
/**
|
||||
* The 48-bit serial number, this value is set when you call {@link begin()}
|
||||
*/
|
||||
uint16_t serialnumber[3];
|
||||
private:
|
||||
TwoWire *_i2c;
|
||||
uint8_t _i2caddr;
|
||||
|
||||
void write(uint8_t address, uint8_t *data, uint8_t n);
|
||||
void read(uint8_t address, uint8_t *data, uint8_t n);
|
||||
boolean readWordFromCommand(uint8_t command[], uint8_t commandLength, uint16_t delay, uint16_t *readdata = NULL, uint8_t readlen = 0);
|
||||
uint8_t generateCRC(uint8_t data[], uint8_t datalen);
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
Adafruit_SGP30
|
||||
================
|
||||
|
||||
This is the Adafruit SGP30 Gas / Air Quality I2C sensor library
|
||||
|
||||
Tested and works great with the Aadafruit SGP30 Breakout Board
|
||||
* http://www.adafruit.com/products/3709
|
||||
|
||||
This chip uses I2C to communicate, 2 pins are required to interface
|
||||
|
||||
Adafruit invests time and resources providing this open source code,
|
||||
please support Adafruit and open-source hardware by purchasing
|
||||
products from Adafruit!
|
||||
|
||||
Written by Limor Fried for Adafruit Industries.
|
||||
BSD license, check license.txt for more information
|
||||
All text above must be included in any redistribution
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
/*!
|
||||
* @file Adafruit_SGP30.cpp
|
||||
*
|
||||
* @mainpage Adafruit SGP30 gas sensor driver
|
||||
*
|
||||
* @section intro_sec Introduction
|
||||
*
|
||||
* This is the documentation for Adafruit's SGP30 driver for the
|
||||
* Arduino platform. It is designed specifically to work with the
|
||||
* Adafruit SGP30 breakout: http://www.adafruit.com/products/3709
|
||||
*
|
||||
* These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
|
||||
* to interface with the breakout.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing
|
||||
* products from Adafruit!
|
||||
*
|
||||
*
|
||||
* @section author Author
|
||||
* Written by Ladyada for Adafruit Industries.
|
||||
*
|
||||
* @section license License
|
||||
* BSD license, all text here must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "Adafruit_SGP30.h"
|
||||
//#define I2C_DEBUG
|
||||
|
||||
/*!
|
||||
* @brief Instantiates a new SGP30 class
|
||||
*/
|
||||
Adafruit_SGP30::Adafruit_SGP30() {}
|
||||
|
||||
/*!
|
||||
* @brief Setups the hardware and detects a valid SGP30. Initializes I2C
|
||||
* then reads the serialnumber and checks that we are talking to an
|
||||
* SGP30
|
||||
* @param theWire
|
||||
* Optional pointer to I2C interface, otherwise use Wire
|
||||
* @param initSensor
|
||||
* Optional pointer to prevent IAQinit to be called. Used for Deep
|
||||
* Sleep.
|
||||
* @return True if SGP30 found on I2C, False if something went wrong!
|
||||
*/
|
||||
boolean Adafruit_SGP30::begin(TwoWire *theWire, boolean initSensor) {
|
||||
_i2caddr = SGP30_I2CADDR_DEFAULT;
|
||||
_i2c = theWire;
|
||||
|
||||
_i2c->begin();
|
||||
|
||||
uint8_t command[2];
|
||||
command[0] = 0x36;
|
||||
command[1] = 0x82;
|
||||
if (!readWordFromCommand(command, 2, 10, serialnumber, 3))
|
||||
return false;
|
||||
|
||||
uint16_t featureset;
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x2F;
|
||||
if (!readWordFromCommand(command, 2, 10, &featureset, 1))
|
||||
return false;
|
||||
// Serial.print("Featureset 0x"); Serial.println(featureset, HEX);
|
||||
if ((featureset & 0xF0) != SGP30_FEATURESET)
|
||||
return false;
|
||||
if (initSensor) {
|
||||
if (!IAQinit())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Commands the sensor to perform a soft reset using the "General
|
||||
* Call" mode. Take note that this is not sensor specific and all devices that
|
||||
* support the General Call mode on the on the same I2C bus will perform this.
|
||||
*
|
||||
* @return True if command completed successfully, false if something went
|
||||
* wrong!
|
||||
*/
|
||||
boolean Adafruit_SGP30::softReset(void) {
|
||||
uint8_t command[2];
|
||||
command[0] = 0x00;
|
||||
command[1] = 0x06;
|
||||
return readWordFromCommand(command, 2, 10);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Commands the sensor to begin the IAQ algorithm. Must be called
|
||||
* after startup.
|
||||
* @returns True if command completed successfully, false if something went
|
||||
* wrong!
|
||||
*/
|
||||
boolean Adafruit_SGP30::IAQinit(void) {
|
||||
uint8_t command[2];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x03;
|
||||
return readWordFromCommand(command, 2, 10);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Commands the sensor to take a single eCO2/VOC measurement. Places
|
||||
* results in {@link TVOC} and {@link eCO2}
|
||||
* @return True if command completed successfully, false if something went
|
||||
* wrong!
|
||||
*/
|
||||
boolean Adafruit_SGP30::IAQmeasure(void) {
|
||||
uint8_t command[2];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x08;
|
||||
uint16_t reply[2];
|
||||
if (!readWordFromCommand(command, 2, 12, reply, 2))
|
||||
return false;
|
||||
TVOC = reply[1];
|
||||
eCO2 = reply[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Commands the sensor to take a single H2/ethanol raw measurement.
|
||||
* Places results in {@link rawH2} and {@link rawEthanol}
|
||||
* @returns True if command completed successfully, false if something went
|
||||
* wrong!
|
||||
*/
|
||||
boolean Adafruit_SGP30::IAQmeasureRaw(void) {
|
||||
uint8_t command[2];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x50;
|
||||
uint16_t reply[2];
|
||||
if (!readWordFromCommand(command, 2, 25, reply, 2))
|
||||
return false;
|
||||
rawEthanol = reply[1];
|
||||
rawH2 = reply[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Request baseline calibration values for both CO2 and TVOC IAQ
|
||||
* calculations. Places results in parameter memory locaitons.
|
||||
* @param eco2_base
|
||||
* A pointer to a uint16_t which we will save the calibration
|
||||
* value to
|
||||
* @param tvoc_base
|
||||
* A pointer to a uint16_t which we will save the calibration value to
|
||||
* @return True if command completed successfully, false if something went
|
||||
* wrong!
|
||||
*/
|
||||
boolean Adafruit_SGP30::getIAQBaseline(uint16_t *eco2_base,
|
||||
uint16_t *tvoc_base) {
|
||||
uint8_t command[2];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x15;
|
||||
uint16_t reply[2];
|
||||
if (!readWordFromCommand(command, 2, 10, reply, 2))
|
||||
return false;
|
||||
*eco2_base = reply[0];
|
||||
*tvoc_base = reply[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Assign baseline calibration values for both CO2 and TVOC IAQ
|
||||
* calculations.
|
||||
* @param eco2_base
|
||||
* A uint16_t which we will save the calibration value from
|
||||
* @param tvoc_base
|
||||
* A uint16_t which we will save the calibration value from
|
||||
* @return True if command completed successfully, false if something went
|
||||
* wrong!
|
||||
*/
|
||||
boolean Adafruit_SGP30::setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base) {
|
||||
uint8_t command[8];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x1e;
|
||||
command[2] = tvoc_base >> 8;
|
||||
command[3] = tvoc_base & 0xFF;
|
||||
command[4] = generateCRC(command + 2, 2);
|
||||
command[5] = eco2_base >> 8;
|
||||
command[6] = eco2_base & 0xFF;
|
||||
command[7] = generateCRC(command + 5, 2);
|
||||
|
||||
return readWordFromCommand(command, 8, 10);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set the absolute humidity value [mg/m^3] for compensation to
|
||||
* increase precision of TVOC and eCO2.
|
||||
* @param absolute_humidity
|
||||
* A uint32_t [mg/m^3] which we will be used for compensation.
|
||||
* If the absolute humidity is set to zero, humidity compensation
|
||||
* will be disabled.
|
||||
* @return True if command completed successfully, false if something went
|
||||
* wrong!
|
||||
*/
|
||||
boolean Adafruit_SGP30::setHumidity(uint32_t absolute_humidity) {
|
||||
if (absolute_humidity > 256000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t ah_scaled =
|
||||
(uint16_t)(((uint64_t)absolute_humidity * 256 * 16777) >> 24);
|
||||
uint8_t command[5];
|
||||
command[0] = 0x20;
|
||||
command[1] = 0x61;
|
||||
command[2] = ah_scaled >> 8;
|
||||
command[3] = ah_scaled & 0xFF;
|
||||
command[4] = generateCRC(command + 2, 2);
|
||||
|
||||
return readWordFromCommand(command, 5, 10);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief I2C low level interfacing
|
||||
*/
|
||||
|
||||
boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[],
|
||||
uint8_t commandLength,
|
||||
uint16_t delayms,
|
||||
uint16_t *readdata,
|
||||
uint8_t readlen) {
|
||||
|
||||
_i2c->beginTransmission(_i2caddr);
|
||||
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("\t\t-> ");
|
||||
#endif
|
||||
|
||||
for (uint8_t i = 0; i < commandLength; i++) {
|
||||
_i2c->write(command[i]);
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("0x");
|
||||
Serial.print(command[i], HEX);
|
||||
Serial.print(", ");
|
||||
#endif
|
||||
}
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.println();
|
||||
#endif
|
||||
_i2c->endTransmission();
|
||||
|
||||
delay(delayms);
|
||||
|
||||
if (readlen == 0)
|
||||
return true;
|
||||
|
||||
uint8_t replylen = readlen * (SGP30_WORD_LEN + 1);
|
||||
if (_i2c->requestFrom(_i2caddr, replylen) != replylen)
|
||||
return false;
|
||||
uint8_t replybuffer[replylen];
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("\t\t<- ");
|
||||
#endif
|
||||
for (uint8_t i = 0; i < replylen; i++) {
|
||||
replybuffer[i] = _i2c->read();
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("0x");
|
||||
Serial.print(replybuffer[i], HEX);
|
||||
Serial.print(", ");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
for (uint8_t i = 0; i < readlen; i++) {
|
||||
uint8_t crc = generateCRC(replybuffer + i * 3, 2);
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("\t\tCRC calced: 0x");
|
||||
Serial.print(crc, HEX);
|
||||
Serial.print(" vs. 0x");
|
||||
Serial.println(replybuffer[i * 3 + 2], HEX);
|
||||
#endif
|
||||
if (crc != replybuffer[i * 3 + 2])
|
||||
return false;
|
||||
// success! store it
|
||||
readdata[i] = replybuffer[i * 3];
|
||||
readdata[i] <<= 8;
|
||||
readdata[i] |= replybuffer[i * 3 + 1];
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("\t\tRead: 0x");
|
||||
Serial.println(readdata[i], HEX);
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t Adafruit_SGP30::generateCRC(uint8_t *data, uint8_t datalen) {
|
||||
// calculates 8-Bit checksum with given polynomial
|
||||
uint8_t crc = SGP30_CRC8_INIT;
|
||||
|
||||
for (uint8_t i = 0; i < datalen; i++) {
|
||||
crc ^= data[i];
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
if (crc & 0x80)
|
||||
crc = (crc << 1) ^ SGP30_CRC8_POLYNOMIAL;
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*!
|
||||
* @file Adafruit_SGP30.h
|
||||
*
|
||||
* This is the documentation for Adafruit's SGP30 driver for the
|
||||
* Arduino platform. It is designed specifically to work with the
|
||||
* Adafruit SGP30 breakout: http://www.adafruit.com/products/3709
|
||||
*
|
||||
* These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
|
||||
* to interface with the breakout.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing
|
||||
* products from Adafruit!
|
||||
*
|
||||
* Written by Ladyada for Adafruit Industries.
|
||||
*
|
||||
* BSD license, all text here must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <Wire.h>
|
||||
|
||||
// the i2c address
|
||||
#define SGP30_I2CADDR_DEFAULT 0x58 ///< SGP30 has only one I2C address
|
||||
|
||||
// commands and constants
|
||||
#define SGP30_FEATURESET 0x0020 ///< The required set for this library
|
||||
#define SGP30_CRC8_POLYNOMIAL 0x31 ///< Seed for SGP30's CRC polynomial
|
||||
#define SGP30_CRC8_INIT 0xFF ///< Init value for CRC
|
||||
#define SGP30_WORD_LEN 2 ///< 2 bytes per word
|
||||
|
||||
/*!
|
||||
* @brief Class that stores state and functions for interacting with
|
||||
* SGP30 Gas Sensor
|
||||
*/
|
||||
class Adafruit_SGP30 {
|
||||
public:
|
||||
Adafruit_SGP30();
|
||||
boolean begin(TwoWire *theWire = &Wire, boolean initSensor = true);
|
||||
boolean softReset();
|
||||
boolean IAQinit();
|
||||
boolean IAQmeasure();
|
||||
boolean IAQmeasureRaw();
|
||||
|
||||
boolean getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base);
|
||||
boolean setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base);
|
||||
boolean setHumidity(uint32_t absolute_humidity);
|
||||
|
||||
/** The last measurement of the IAQ-calculated Total Volatile Organic
|
||||
* Compounds in ppb. This value is set when you call {@link IAQmeasure()} **/
|
||||
uint16_t TVOC;
|
||||
|
||||
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
|
||||
* value is set when you call {@link IAQmeasure()} **/
|
||||
uint16_t eCO2;
|
||||
|
||||
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
|
||||
* value is set when you call {@link IAQmeasureRaw()} **/
|
||||
uint16_t rawH2;
|
||||
|
||||
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
|
||||
* value is set when you call {@link IAQmeasureRaw()} **/
|
||||
uint16_t rawEthanol;
|
||||
|
||||
/** The 48-bit serial number, this value is set when you call {@link begin()}
|
||||
* **/
|
||||
uint16_t serialnumber[3];
|
||||
|
||||
private:
|
||||
TwoWire *_i2c;
|
||||
uint8_t _i2caddr;
|
||||
|
||||
void write(uint8_t address, uint8_t *data, uint8_t n);
|
||||
void read(uint8_t address, uint8_t *data, uint8_t n);
|
||||
boolean readWordFromCommand(uint8_t command[], uint8_t commandLength,
|
||||
uint16_t delay, uint16_t *readdata = NULL,
|
||||
uint8_t readlen = 0);
|
||||
uint8_t generateCRC(uint8_t data[], uint8_t datalen);
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
# Adafruit SGP30 Gas / Air Quality I2C sensor [[![Build Status](https://github.com/adafruit/Adafruit_SGP30/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_SGP30/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_SGP30/html/index.html)
|
||||
|
||||
<a href="https://www.adafruit.com/product/3709"><img src="assets/board.jpg?raw=true" width="500px"></a>
|
||||
|
||||
This is the Adafruit SGP30 Gas / Air Quality I2C sensor library
|
||||
|
||||
Tested and works great with the Aadafruit SGP30 Breakout Board
|
||||
* http://www.adafruit.com/products/3709
|
||||
|
||||
This chip uses I2C to communicate, 2 pins are required to interface
|
||||
|
||||
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
|
||||
|
||||
# Installation
|
||||
To install, use the Arduino Library Manager and search for "Adafruit SGP30" and install the library.
|
||||
|
||||
## Dependencies
|
||||
* [Adafruit ILI9341](https://github.com/adafruit/Adafruit_ILI9341)
|
||||
* [Adafruit GFX Library](https://github.com/adafruit/Adafruit-GFX-Library)
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions are welcome! Please read our [Code of Conduct](https://github.com/adafruit/Adafruit_SGP30/blob/master/CODE_OF_CONDUCT.md>)
|
||||
before contributing to help this project stay welcoming.
|
||||
|
||||
## Documentation and doxygen
|
||||
Documentation is produced by doxygen. Contributions should include documentation for any new code added.
|
||||
|
||||
Some examples of how to use doxygen can be found in these guide pages:
|
||||
|
||||
https://learn.adafruit.com/the-well-automated-arduino-library/doxygen
|
||||
|
||||
https://learn.adafruit.com/the-well-automated-arduino-library/doxygen-tips
|
||||
|
||||
## Formatting and clang-format
|
||||
This library uses [`clang-format`](https://releases.llvm.org/download.html) to standardize the formatting of `.cpp` and `.h` files.
|
||||
Contributions should be formatted using `clang-format`:
|
||||
|
||||
The `-i` flag will make the changes to the file.
|
||||
```bash
|
||||
clang-format -i *.cpp *.h
|
||||
```
|
||||
If you prefer to make the changes yourself, running `clang-format` without the `-i` flag will print out a formatted version of the file. You can save this to a file and diff it against the original to see the changes.
|
||||
|
||||
Note that the formatting output by `clang-format` is what the automated formatting checker will expect. Any diffs from this formatting will result in a failed build until they are addressed. Using the `-i` flag is highly recommended.
|
||||
|
||||
### clang-format resources
|
||||
* [Binary builds and source available on the LLVM downloads page](https://releases.llvm.org/download.html)
|
||||
* [Documentation and IDE integration](https://clang.llvm.org/docs/ClangFormat.html)
|
||||
|
||||
## About this Driver
|
||||
Written by Limor Fried for Adafruit Industries.
|
||||
BSD license, check license.txt for more information
|
||||
All text above must be included in any redistribution
|
10
lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino → lib/Adafruit_SGP30-1.2.0/examples/sgp30test/sgp30test.ino
Executable file → Normal file
10
lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino → lib/Adafruit_SGP30-1.2.0/examples/sgp30test/sgp30test.ino
Executable file → Normal file
|
@ -44,6 +44,14 @@ void loop() {
|
|||
}
|
||||
Serial.print("TVOC "); Serial.print(sgp.TVOC); Serial.print(" ppb\t");
|
||||
Serial.print("eCO2 "); Serial.print(sgp.eCO2); Serial.println(" ppm");
|
||||
|
||||
if (! sgp.IAQmeasureRaw()) {
|
||||
Serial.println("Raw Measurement failed");
|
||||
return;
|
||||
}
|
||||
Serial.print("Raw H2 "); Serial.print(sgp.rawH2); Serial.print(" \t");
|
||||
Serial.print("Raw Ethanol "); Serial.print(sgp.rawEthanol); Serial.println("");
|
||||
|
||||
delay(1000);
|
||||
|
||||
counter++;
|
||||
|
@ -58,4 +66,4 @@ void loop() {
|
|||
Serial.print("****Baseline values: eCO2: 0x"); Serial.print(eCO2_base, HEX);
|
||||
Serial.print(" & TVOC: 0x"); Serial.println(TVOC_base, HEX);
|
||||
}
|
||||
}
|
||||
}
|
3
lib/Adafruit_SGP30-1.0.3/library.properties → lib/Adafruit_SGP30-1.2.0/library.properties
Executable file → Normal file
3
lib/Adafruit_SGP30-1.0.3/library.properties → lib/Adafruit_SGP30-1.2.0/library.properties
Executable file → Normal file
|
@ -1,5 +1,5 @@
|
|||
name=Adafruit SGP30 Sensor
|
||||
version=1.0.3
|
||||
version=1.2.0
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
sentence=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor
|
||||
|
@ -7,3 +7,4 @@ paragraph=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Se
|
|||
category=Sensors
|
||||
url=https://github.com/adafruit/Adafruit_SGP30
|
||||
architectures=*
|
||||
depends=Adafruit ILI9341, Adafruit GFX Library
|
0
lib/Adafruit_SGP30-1.0.3/license.txt → lib/Adafruit_SGP30-1.2.0/license.txt
Executable file → Normal file
0
lib/Adafruit_SGP30-1.0.3/license.txt → lib/Adafruit_SGP30-1.2.0/license.txt
Executable file → Normal file
0
lib/Adafruit_SGP30-1.0.3/.travis.yml → lib/Adafruit_SGP30-1.2.0/travis.yml
Executable file → Normal file
0
lib/Adafruit_SGP30-1.0.3/.travis.yml → lib/Adafruit_SGP30-1.2.0/travis.yml
Executable file → Normal file
|
@ -76,6 +76,29 @@ boolean Adafruit_VEML7700::begin(TwoWire *theWire) {
|
|||
return true;
|
||||
}
|
||||
|
||||
float Adafruit_VEML7700::alternate_pow(float a, float b)
|
||||
{
|
||||
// https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/
|
||||
// calculate approximation with fraction of the exponent
|
||||
int e = abs((int)b);
|
||||
union {
|
||||
double d;
|
||||
int x[2];
|
||||
} u = { a };
|
||||
u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447);
|
||||
u.x[0] = 0;
|
||||
// exponentiation by squaring with the exponent's integer part
|
||||
// double r = u.d makes everything much slower, not sure why
|
||||
double r = 1.0;
|
||||
while (e) {
|
||||
if (e & 1) {
|
||||
r *= a;
|
||||
}
|
||||
a *= a;
|
||||
e >>= 1;
|
||||
}
|
||||
return r * u.d;
|
||||
}
|
||||
|
||||
float Adafruit_VEML7700::normalize_resolution(float value) {
|
||||
// adjust for gain (1x is normalized)
|
||||
|
@ -123,7 +146,7 @@ float Adafruit_VEML7700::readLuxNormalized() {
|
|||
// user-provided correction for non-linearities at high lux/white values:
|
||||
// https://forums.adafruit.com/viewtopic.php?f=19&t=152997&p=758582#p759346
|
||||
if ((getGain() == VEML7700_GAIN_1_8) && (getIntegrationTime() == VEML7700_IT_25MS)){
|
||||
lux = 6.0135e-13*pow(lux,4) - 9.3924e-9*pow(lux,3) + 8.1488e-5*pow(lux,2) + 1.0023*lux;
|
||||
lux = 6.0135e-13*alternate_pow(lux,4) - 9.3924e-9*alternate_pow(lux,3) + 8.1488e-5*alternate_pow(lux,2) + 1.0023*lux;
|
||||
}
|
||||
|
||||
return lux;
|
||||
|
@ -156,7 +179,7 @@ float Adafruit_VEML7700::readWhiteNormalized() {
|
|||
// user-provided correction for non-linearities at high lux values:
|
||||
// https://forums.adafruit.com/viewtopic.php?f=19&t=152997&p=758582#p759346
|
||||
if ((getGain() == VEML7700_GAIN_1_8) && (getIntegrationTime() == VEML7700_IT_25MS)){
|
||||
white = 2E-15*pow(white,4) + 4E-12*pow(white,3) + 9E-06*pow(white,2) + 1.0179*white - 11.052;
|
||||
white = 2E-15*alternate_pow(white,4) + 4E-12*alternate_pow(white,3) + 9E-06*alternate_pow(white,2) + 1.0179*white - 11.052;
|
||||
}
|
||||
|
||||
return white;
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
* BSD license (see license.txt)
|
||||
*/
|
||||
|
||||
/*
|
||||
* change from device111 for Tasmota
|
||||
* Add alternativ Pow function for readLuxNormalized() and readWhiteNormalized()
|
||||
*/
|
||||
|
||||
#ifndef _ADAFRUIT_VEML7700_H
|
||||
#define _ADAFRUIT_VEML7700_H
|
||||
|
||||
|
@ -105,6 +110,7 @@ private:
|
|||
*PowerSave_Enable, *PowerSave_Mode;
|
||||
|
||||
float normalize_resolution(float value);
|
||||
float alternate_pow(float a, float b);
|
||||
|
||||
Adafruit_I2CDevice *i2c_dev;
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ extra_scripts = ${scripts_defaults.extra_scripts}
|
|||
[tasmota_stage]
|
||||
; *** Esp8266 core for Arduino version Tasmota stage
|
||||
extends = tasmota_core
|
||||
platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#a5432625d93f60d7e28cfdc5ed8abb3e0151951d
|
||||
platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#52b3e5b7b3ccedcede665682f7896b637b64dbf5
|
||||
; *********** Alternative Options, enable only if you know exactly what you do ********
|
||||
; NONOSDK221
|
||||
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
- Change Energy JSON Total field from ``"Total":[33.736,11.717,16.978]`` to ``"Total":33.736,"TotalTariff":[11.717,16.978]``
|
||||
- Change Energy JSON ExportActive field from ``"ExportActive":[33.736,11.717,16.978]`` to ``"ExportActive":33.736,"ExportTariff":[11.717,16.978]``
|
||||
- Change Adafruit_SGP30 library from v1.0.3 to v1.2.0 (#8519)
|
||||
- Add Three Phase Export Active Energy to SDM630 driver
|
||||
- Add commands ``LedPwmOn 0..255``, ``LedPwmOff 0..255`` and ``LedPwmMode1 0/1`` to control led brightness by George (#8491)
|
||||
- Add wildcard pattern ``?`` for JSON matching in rules
|
||||
|
||||
### 8.3.1.1 20200518
|
||||
|
||||
|
|
|
@ -295,9 +295,9 @@
|
|||
#define D_CMND_LEDPOWER "LedPower"
|
||||
#define D_CMND_LEDSTATE "LedState"
|
||||
#define D_CMND_LEDMASK "LedMask"
|
||||
#define D_CMND_LEDPWMOFF "LedPwmOff"
|
||||
#define D_CMND_LEDPWMON "LedPwmOn"
|
||||
#define D_CMND_LEDPWMMODE "LedPwmMode"
|
||||
#define D_CMND_LEDPWM_OFF "LedPwmOff"
|
||||
#define D_CMND_LEDPWM_ON "LedPwmOn"
|
||||
#define D_CMND_LEDPWM_MODE "LedPwmMode"
|
||||
#define D_CMND_WIFIPOWER "WifiPower"
|
||||
#define D_CMND_SPEEDUNIT "SpeedUnit"
|
||||
#define D_CMND_I2CSCAN "I2CScan"
|
||||
|
|
|
@ -1874,65 +1874,6 @@ void AddLogBufferSize(uint32_t loglevel, uint8_t *buffer, uint32_t count, uint32
|
|||
AddLog(loglevel);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* JSON parsing
|
||||
\*********************************************************************************************/
|
||||
|
||||
// does the character needs to be escaped, and if so with which character
|
||||
char escapeJSONChar(char c) {
|
||||
if ((c == '\"') || (c == '\\')) {
|
||||
return c;
|
||||
}
|
||||
if (c == '\n') { return 'n'; }
|
||||
if (c == '\t') { return 't'; }
|
||||
if (c == '\r') { return 'r'; }
|
||||
if (c == '\f') { return 'f'; }
|
||||
if (c == '\b') { return 'b'; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
String escapeJSONString(const char *str) {
|
||||
String r("");
|
||||
if (nullptr == str) { return r; }
|
||||
|
||||
bool needs_escape = false;
|
||||
size_t len_out = 1;
|
||||
const char * c = str;
|
||||
|
||||
while (*c) {
|
||||
if (escapeJSONChar(*c)) {
|
||||
len_out++;
|
||||
needs_escape = true;
|
||||
}
|
||||
c++;
|
||||
len_out++;
|
||||
}
|
||||
|
||||
if (needs_escape) {
|
||||
// we need to escape some chars
|
||||
// allocate target buffer
|
||||
r.reserve(len_out);
|
||||
c = str;
|
||||
char *d = r.begin();
|
||||
while (*c) {
|
||||
char c2 = escapeJSONChar(*c);
|
||||
if (c2) {
|
||||
c++;
|
||||
*d++ = '\\';
|
||||
*d++ = c2;
|
||||
} else {
|
||||
*d++ = *c++;
|
||||
}
|
||||
}
|
||||
*d = 0; // add NULL terminator
|
||||
r = (char*) r.begin(); // assign the buffer to the string
|
||||
} else {
|
||||
r = str;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Uncompress static PROGMEM strings
|
||||
\*********************************************************************************************/
|
||||
|
|
|
@ -26,8 +26,8 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
|
|||
D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|"
|
||||
D_CMND_SERIALDELIMITER "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|"
|
||||
D_CMND_DEVICENAME "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|"
|
||||
D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|"
|
||||
D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM "|" D_CMND_LEDPWMON "|" D_CMND_LEDPWMOFF "|" D_CMND_LEDPWMMODE "|"
|
||||
D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_LEDPWM_ON "|" D_CMND_LEDPWM_OFF "|" D_CMND_LEDPWM_MODE "|"
|
||||
D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM "|"
|
||||
#ifdef USE_I2C
|
||||
D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|"
|
||||
#endif
|
||||
|
@ -49,8 +49,8 @@ void (* const TasmotaCommand[])(void) PROGMEM = {
|
|||
&CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig,
|
||||
&CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig,
|
||||
&CmndDevicename, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd,
|
||||
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, &CmndHumOffset,
|
||||
&CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode,
|
||||
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode,
|
||||
&CmndWifiPower, &CmndTempOffset, &CmndHumOffset, &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum,
|
||||
#ifdef USE_I2C
|
||||
&CmndI2cScan, CmndI2cDriver,
|
||||
#endif
|
||||
|
@ -1804,6 +1804,62 @@ void CmndLedMask(void)
|
|||
ResponseCmndChar(stemp1);
|
||||
}
|
||||
|
||||
void CmndLedPwmOff(void)
|
||||
{
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if (XdrvMailbox.payload < 0) {
|
||||
Settings.ledpwm_off = 0;
|
||||
}
|
||||
else if (XdrvMailbox.payload > 255) {
|
||||
Settings.ledpwm_off = 255;
|
||||
} else {
|
||||
Settings.ledpwm_off = XdrvMailbox.payload;
|
||||
}
|
||||
UpdateLedPowerAll();
|
||||
}
|
||||
ResponseCmndNumber(Settings.ledpwm_off);
|
||||
}
|
||||
|
||||
void CmndLedPwmOn(void)
|
||||
{
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if (XdrvMailbox.payload < 0) {
|
||||
Settings.ledpwm_on = 0;
|
||||
}
|
||||
else if (XdrvMailbox.payload > 255) {
|
||||
Settings.ledpwm_on = 255;
|
||||
} else {
|
||||
Settings.ledpwm_on = XdrvMailbox.payload;
|
||||
}
|
||||
UpdateLedPowerAll();
|
||||
}
|
||||
ResponseCmndNumber(Settings.ledpwm_on);
|
||||
}
|
||||
|
||||
void CmndLedPwmMode(void)
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) {
|
||||
if (!PinUsed(GPIO_LEDLNK)) { XdrvMailbox.index = 1; }
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
|
||||
uint32_t mask = 1 << (XdrvMailbox.index -1); // Led to configure
|
||||
switch (XdrvMailbox.payload) {
|
||||
case 0: // digital
|
||||
Settings.ledpwm_mask &= (0xFF ^ mask);
|
||||
break;
|
||||
case 1: // pwm
|
||||
Settings.ledpwm_mask |= mask;
|
||||
break;
|
||||
case 2: // toggle
|
||||
Settings.ledpwm_mask ^= mask;
|
||||
break;
|
||||
}
|
||||
UpdateLedPowerAll();
|
||||
}
|
||||
bool state = bitRead(Settings.ledpwm_mask, XdrvMailbox.index -1);
|
||||
ResponseCmndIdxChar(GetStateText(state));
|
||||
}
|
||||
}
|
||||
|
||||
void CmndWifiPower(void)
|
||||
{
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
|
@ -1890,59 +1946,3 @@ void CmndDriver(void)
|
|||
{
|
||||
XdrvCall(FUNC_COMMAND_DRIVER);
|
||||
}
|
||||
|
||||
void CmndLedPwmOff(void)
|
||||
{
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if (XdrvMailbox.payload < 0) {
|
||||
Settings.ledpwm_off = 0;
|
||||
}
|
||||
else if (XdrvMailbox.payload > 255) {
|
||||
Settings.ledpwm_off = 255;
|
||||
} else {
|
||||
Settings.ledpwm_off = XdrvMailbox.payload;
|
||||
}
|
||||
UpdateLedPowerAll();
|
||||
}
|
||||
ResponseCmndNumber(Settings.ledpwm_off);
|
||||
}
|
||||
|
||||
void CmndLedPwmOn(void)
|
||||
{
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if (XdrvMailbox.payload < 0) {
|
||||
Settings.ledpwm_on = 0;
|
||||
}
|
||||
else if (XdrvMailbox.payload > 255) {
|
||||
Settings.ledpwm_on = 255;
|
||||
} else {
|
||||
Settings.ledpwm_on = XdrvMailbox.payload;
|
||||
}
|
||||
UpdateLedPowerAll();
|
||||
}
|
||||
ResponseCmndNumber(Settings.ledpwm_on);
|
||||
}
|
||||
|
||||
void CmndLedPwmMode(void)
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) {
|
||||
if (!PinUsed(GPIO_LEDLNK)) { XdrvMailbox.index = 1; }
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
|
||||
uint32_t mask = 1 << (XdrvMailbox.index -1); // Led to configure
|
||||
switch (XdrvMailbox.payload) {
|
||||
case 0: // digital
|
||||
Settings.ledpwm_mask &= (0xFF ^ mask);
|
||||
break;
|
||||
case 1: // pwm
|
||||
Settings.ledpwm_mask |= mask;
|
||||
break;
|
||||
case 2: // toggle
|
||||
Settings.ledpwm_mask ^= mask;
|
||||
break;
|
||||
}
|
||||
UpdateLedPowerAll();
|
||||
}
|
||||
bool state = bitRead(Settings.ledpwm_mask, XdrvMailbox.index -1);
|
||||
ResponseCmndIdxChar(GetStateText(state));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
support_json.ino - JSON support functions
|
||||
|
||||
Copyright (C) 2020 Theo Arends and Stephan Hadinger
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*********************************************************************************************\
|
||||
* JSON parsing
|
||||
\*********************************************************************************************/
|
||||
|
||||
// does the character needs to be escaped, and if so with which character
|
||||
char EscapeJSONChar(char c) {
|
||||
if ((c == '\"') || (c == '\\')) {
|
||||
return c;
|
||||
}
|
||||
if (c == '\n') { return 'n'; }
|
||||
if (c == '\t') { return 't'; }
|
||||
if (c == '\r') { return 'r'; }
|
||||
if (c == '\f') { return 'f'; }
|
||||
if (c == '\b') { return 'b'; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
String EscapeJSONString(const char *str) {
|
||||
String r("");
|
||||
if (nullptr == str) { return r; }
|
||||
|
||||
bool needs_escape = false;
|
||||
size_t len_out = 1;
|
||||
const char * c = str;
|
||||
|
||||
while (*c) {
|
||||
if (EscapeJSONChar(*c)) {
|
||||
len_out++;
|
||||
needs_escape = true;
|
||||
}
|
||||
c++;
|
||||
len_out++;
|
||||
}
|
||||
|
||||
if (needs_escape) {
|
||||
// we need to escape some chars
|
||||
// allocate target buffer
|
||||
r.reserve(len_out);
|
||||
c = str;
|
||||
char *d = r.begin();
|
||||
while (*c) {
|
||||
char c2 = EscapeJSONChar(*c);
|
||||
if (c2) {
|
||||
c++;
|
||||
*d++ = '\\';
|
||||
*d++ = c2;
|
||||
} else {
|
||||
*d++ = *c++;
|
||||
}
|
||||
}
|
||||
*d = 0; // add NULL terminator
|
||||
r = (char*) r.begin(); // assign the buffer to the string
|
||||
} else {
|
||||
r = str;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Find key - case insensitive
|
||||
\*********************************************************************************************/
|
||||
|
||||
// Given a JsonObject, finds the value as JsonVariant for the key needle.
|
||||
// The search is case-insensitive, and will find the first match in the order of keys in JSON
|
||||
//
|
||||
// If the key is not found, returns a nullptr
|
||||
// Input: needle cannot be NULL but may be PROGMEM
|
||||
const JsonVariant &GetCaseInsensitive(const JsonObject &json, const char *needle) {
|
||||
// key can be in PROGMEM
|
||||
// if needle == "?" then we return the first valid key
|
||||
bool wildcard = strcmp_P("?", needle) == 0;
|
||||
if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) {
|
||||
return *(JsonVariant*)nullptr;
|
||||
}
|
||||
|
||||
for (JsonObject::const_iterator it=json.begin(); it!=json.end(); ++it) {
|
||||
const char *key = it->key;
|
||||
const JsonVariant &value = it->value;
|
||||
|
||||
if (wildcard || (0 == strcasecmp_P(key, needle))) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
// if not found
|
||||
return *(JsonVariant*)nullptr;
|
||||
}
|
|
@ -1291,16 +1291,23 @@ void SerialInput(void)
|
|||
}
|
||||
} else {
|
||||
if (serial_in_byte || Settings.flag.mqtt_serial_raw) { // Any char between 1 and 127 or any char (0 - 255) - CMND_SERIALSEND3
|
||||
bool in_byte_is_delimiter = // Char is delimiter when...
|
||||
(((Settings.serial_delimiter < 128) && (serial_in_byte == Settings.serial_delimiter)) || // Any char between 1 and 127 and being delimiter
|
||||
((Settings.serial_delimiter == 128) && !isprint(serial_in_byte))) && // Any char not between 32 and 127
|
||||
!Settings.flag.mqtt_serial_raw; // In raw mode (CMND_SERIALSEND3) there is never a delimiter
|
||||
|
||||
if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && // Add char to string if it still fits and ...
|
||||
((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || // Any char between 32 and 127
|
||||
((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || // Any char between 1 and 127 and not being delimiter
|
||||
Settings.flag.mqtt_serial_raw)) { // Any char between 0 and 255 - CMND_SERIALSEND3
|
||||
!in_byte_is_delimiter) { // Char is not a delimiter
|
||||
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
|
||||
serial_polling_window = millis();
|
||||
} else {
|
||||
}
|
||||
|
||||
if ((serial_in_byte_counter >= INPUT_BUFFER_SIZE -1) || // Send message when buffer is full or ...
|
||||
in_byte_is_delimiter) { // Char is delimiter
|
||||
serial_polling_window = 0; // Reception done - send mqtt
|
||||
break;
|
||||
}
|
||||
|
||||
serial_polling_window = millis(); // Wait for next char
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,17 +54,23 @@ void SerialBridgeInput(void)
|
|||
return;
|
||||
}
|
||||
if (serial_in_byte || serial_bridge_raw) { // Any char between 1 and 127 or any char (0 - 255)
|
||||
bool in_byte_is_delimiter = // Char is delimiter when...
|
||||
(((Settings.serial_delimiter < 128) && (serial_in_byte == Settings.serial_delimiter)) || // Any char between 1 and 127 and being delimiter
|
||||
((Settings.serial_delimiter == 128) && !isprint(serial_in_byte))) && // Any char not between 32 and 127
|
||||
!serial_bridge_raw; // In raw mode (CMND_SERIALSEND3) there is never a delimiter
|
||||
|
||||
if ((serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE -1) && // Add char to string if it still fits and ...
|
||||
((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || // Any char between 32 and 127
|
||||
((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || // Any char between 1 and 127 and not being delimiter
|
||||
serial_bridge_raw)) { // Any char between 0 and 255
|
||||
!in_byte_is_delimiter) { // Char is not a delimiter
|
||||
serial_bridge_buffer[serial_bridge_in_byte_counter++] = serial_in_byte;
|
||||
serial_bridge_polling_window = millis(); // Wait for more data
|
||||
} else {
|
||||
}
|
||||
|
||||
if ((serial_bridge_in_byte_counter >= SERIAL_BRIDGE_BUFFER_SIZE -1) || // Send message when buffer is full or ...
|
||||
in_byte_is_delimiter) { // Char is delimiter
|
||||
serial_bridge_polling_window = 0; // Publish now
|
||||
break;
|
||||
}
|
||||
|
||||
serial_bridge_polling_window = millis(); // Wait for more data
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -441,25 +441,20 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
|||
break;
|
||||
}
|
||||
}
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR("%%TIME%%"));
|
||||
if (rule_param.startsWith(stemp)) {
|
||||
if (rule_param.startsWith(F("%TIME%"))) {
|
||||
rule_param = String(MinutesPastMidnight());
|
||||
}
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR("%%UPTIME%%"));
|
||||
if (rule_param.startsWith(stemp)) {
|
||||
if (rule_param.startsWith(F("%UPTIME%"))) {
|
||||
rule_param = String(MinutesUptime());
|
||||
}
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR("%%TIMESTAMP%%"));
|
||||
if (rule_param.startsWith(stemp)) {
|
||||
if (rule_param.startsWith(F("%TIMESTAMP%"))) {
|
||||
rule_param = GetDateAndTime(DT_LOCAL).c_str();
|
||||
}
|
||||
#if defined(USE_TIMERS) && defined(USE_SUNRISE)
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNRISE%%"));
|
||||
if (rule_param.startsWith(stemp)) {
|
||||
if (rule_param.startsWith(F("%SUNRISE%"))) {
|
||||
rule_param = String(SunMinutes(0));
|
||||
}
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNSET%%"));
|
||||
if (rule_param.startsWith(stemp)) {
|
||||
if (rule_param.startsWith(F("%SUNSET%"))) {
|
||||
rule_param = String(SunMinutes(1));
|
||||
}
|
||||
#endif // USE_TIMERS and USE_SUNRISE
|
||||
|
@ -493,13 +488,17 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
|||
uint32_t i = 0;
|
||||
while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT"
|
||||
subtype = rule_name.substring(0, pos);
|
||||
if (!(*obj)[subtype].success()) { return false; } // No subtype in JSON data
|
||||
JsonObject &obj2 = (*obj)[subtype];
|
||||
obj = &obj2;
|
||||
const JsonVariant & val = GetCaseInsensitive(*obj, subtype.c_str());
|
||||
if (nullptr == &val) { return false; } // not found
|
||||
obj = &(val.as<JsonObject>());
|
||||
if (!obj->success()) { return false; } // not a JsonObject
|
||||
|
||||
rule_name = rule_name.substring(pos +1);
|
||||
if (i++ > 10) { return false; } // Abandon possible loop
|
||||
}
|
||||
if (!(*obj)[rule_name].success()) { return false; } // No name in JSON data
|
||||
|
||||
const JsonVariant & val = GetCaseInsensitive(*obj, rule_name.c_str());
|
||||
if (nullptr == &val) { return false; } // last level not found
|
||||
const char* str_value;
|
||||
if (rule_name_idx) {
|
||||
str_value = (*obj)[rule_name][rule_name_idx -1]; // "CURRENT[1]"
|
||||
|
@ -2036,7 +2035,7 @@ void CmndRule(void)
|
|||
XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)),
|
||||
GetStateText(bitRead(Settings.rule_stop, index -1)),
|
||||
rule_len, MAX_RULE_SIZE - GetRuleLenStorage(index - 1),
|
||||
escapeJSONString(rule.c_str()).c_str());
|
||||
EscapeJSONString(rule.c_str()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -437,8 +437,8 @@ void HueLightStatus2(uint8_t device, String *response)
|
|||
fname[fname_len] = 0x00;
|
||||
}
|
||||
snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2,
|
||||
escapeJSONString(fname).c_str(),
|
||||
escapeJSONString(Settings.user_template_name).c_str(),
|
||||
EscapeJSONString(fname).c_str(),
|
||||
EscapeJSONString(Settings.user_template_name).c_str(),
|
||||
PSTR("Tasmota"),
|
||||
GetHueDeviceId(device).c_str());
|
||||
*response += buf;
|
||||
|
|
|
@ -23,29 +23,9 @@
|
|||
|
||||
void ZigbeeZCLSend_Raw(uint16_t dtsAddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId);
|
||||
|
||||
|
||||
// Get an JSON attribute, with case insensitive key search
|
||||
const JsonVariant &getCaseInsensitive(const JsonObject &json, const char *needle) {
|
||||
// key can be in PROGMEM
|
||||
if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) {
|
||||
return *(JsonVariant*)nullptr;
|
||||
}
|
||||
|
||||
for (JsonObject::const_iterator it=json.begin(); it!=json.end(); ++it) {
|
||||
const char *key = it->key;
|
||||
const JsonVariant &value = it->value;
|
||||
|
||||
if (0 == strcasecmp_P(key, needle)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
// if not found
|
||||
return *(JsonVariant*)nullptr;
|
||||
}
|
||||
|
||||
// get the result as a string (const char*) and nullptr if there is no field or the string is empty
|
||||
const char * getCaseInsensitiveConstCharNull(const JsonObject &json, const char *needle) {
|
||||
const JsonVariant &val = getCaseInsensitive(json, needle);
|
||||
const JsonVariant &val = GetCaseInsensitive(json, needle);
|
||||
if (&val) {
|
||||
const char *val_cs = val.as<const char*>();
|
||||
if (strlen(val_cs)) {
|
||||
|
|
|
@ -1075,7 +1075,7 @@ int32_t Z_Devices::deviceRestore(const JsonObject &json) {
|
|||
size_t endpoints_len = 0;
|
||||
|
||||
// read mandatory "Device"
|
||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
|
||||
if (nullptr != &val_device) {
|
||||
device = strToUInt(val_device);
|
||||
} else {
|
||||
|
@ -1083,7 +1083,7 @@ int32_t Z_Devices::deviceRestore(const JsonObject &json) {
|
|||
}
|
||||
|
||||
// read "IEEEAddr" 64 bits in format "0x0000000000000000"
|
||||
const JsonVariant &val_ieeeaddr = getCaseInsensitive(json, PSTR("IEEEAddr"));
|
||||
const JsonVariant &val_ieeeaddr = GetCaseInsensitive(json, PSTR("IEEEAddr"));
|
||||
if (nullptr != &val_ieeeaddr) {
|
||||
ieeeaddr = strtoull(val_ieeeaddr.as<const char*>(), nullptr, 0);
|
||||
}
|
||||
|
@ -1098,7 +1098,7 @@ int32_t Z_Devices::deviceRestore(const JsonObject &json) {
|
|||
manufid = getCaseInsensitiveConstCharNull(json, PSTR("Manufacturer"));
|
||||
|
||||
// read "Light"
|
||||
const JsonVariant &val_bulbtype = getCaseInsensitive(json, PSTR(D_JSON_ZIGBEE_LIGHT));
|
||||
const JsonVariant &val_bulbtype = GetCaseInsensitive(json, PSTR(D_JSON_ZIGBEE_LIGHT));
|
||||
if (nullptr != &val_bulbtype) { bulbtype = strToUInt(val_bulbtype);; }
|
||||
|
||||
// update internal device information
|
||||
|
@ -1109,7 +1109,7 @@ int32_t Z_Devices::deviceRestore(const JsonObject &json) {
|
|||
if (&val_bulbtype) { setHueBulbtype(device, bulbtype); }
|
||||
|
||||
// read "Endpoints"
|
||||
const JsonVariant &val_endpoints = getCaseInsensitive(json, PSTR("Endpoints"));
|
||||
const JsonVariant &val_endpoints = GetCaseInsensitive(json, PSTR("Endpoints"));
|
||||
if ((nullptr != &val_endpoints) && (val_endpoints.is<JsonArray>())) {
|
||||
const JsonArray &arr_ep = val_endpoints.as<const JsonArray&>();
|
||||
endpoints_len = arr_ep.size();
|
||||
|
|
|
@ -85,9 +85,9 @@ void HueLightStatus2Zigbee(uint16_t shortaddr, String *response)
|
|||
snprintf_P(shortaddrname, sizeof(shortaddrname), PSTR("0x%04X"), shortaddr);
|
||||
|
||||
snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2,
|
||||
(friendlyName) ? escapeJSONString(friendlyName).c_str() : shortaddrname,
|
||||
(modelId) ? escapeJSONString(modelId).c_str() : PSTR("Unknown"),
|
||||
(manufacturerId) ? escapeJSONString(manufacturerId).c_str() : PSTR("Tasmota"),
|
||||
(friendlyName) ? EscapeJSONString(friendlyName).c_str() : shortaddrname,
|
||||
(modelId) ? EscapeJSONString(modelId).c_str() : PSTR("Unknown"),
|
||||
(manufacturerId) ? EscapeJSONString(manufacturerId).c_str() : PSTR("Tasmota"),
|
||||
GetHueDeviceId(shortaddr).c_str());
|
||||
|
||||
*response += buf;
|
||||
|
|
|
@ -600,7 +600,7 @@ void Z_SendAFInfoRequest(uint16_t shortaddr) {
|
|||
void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint8_t endpoint, const JsonObject &json) {
|
||||
static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s
|
||||
// Read OCCUPANCY value if any
|
||||
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR(OCCUPANCY));
|
||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(OCCUPANCY));
|
||||
if (nullptr != &val_endpoint) {
|
||||
uint32_t occupancy = strToUInt(val_endpoint);
|
||||
|
||||
|
|
|
@ -427,13 +427,13 @@ void CmndZbSend(void) {
|
|||
bool clusterSpecific = true;
|
||||
|
||||
// parse JSON
|
||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
|
||||
if (nullptr != &val_device) {
|
||||
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
||||
}
|
||||
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
|
||||
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
|
||||
const JsonVariant &val_group = GetCaseInsensitive(json, PSTR("Group"));
|
||||
if (nullptr != &val_group) {
|
||||
groupaddr = strToUInt(val_group);
|
||||
} else { // no device nor group
|
||||
|
@ -442,11 +442,11 @@ void CmndZbSend(void) {
|
|||
}
|
||||
}
|
||||
|
||||
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
|
||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
|
||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||
const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf"));
|
||||
const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR("Manuf"));
|
||||
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
|
||||
const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send"));
|
||||
const JsonVariant &val_cmd = GetCaseInsensitive(json, PSTR("Send"));
|
||||
if (nullptr != &val_cmd) {
|
||||
// probe the type of the argument
|
||||
// If JSON object, it's high level commands
|
||||
|
@ -582,7 +582,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
|||
|
||||
// Information about source device: "Device", "Endpoint", "Cluster"
|
||||
// - the source endpoint must have a known IEEE address
|
||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
|
||||
if (nullptr != &val_device) {
|
||||
srcDevice = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||
}
|
||||
|
@ -591,17 +591,17 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
|||
uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice);
|
||||
if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; }
|
||||
// look for source endpoint
|
||||
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
|
||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
|
||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||
// look for source cluster
|
||||
const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
|
||||
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR("Cluster"));
|
||||
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
||||
|
||||
// Either Device address
|
||||
// In this case the following parameters are mandatory
|
||||
// - "ToDevice" and the device must have a known IEEE address
|
||||
// - "ToEndpoint"
|
||||
const JsonVariant &dst_device = getCaseInsensitive(json, PSTR("ToDevice"));
|
||||
const JsonVariant &dst_device = GetCaseInsensitive(json, PSTR("ToDevice"));
|
||||
if (nullptr != &dst_device) {
|
||||
dstDevice = zigbee_devices.parseDeviceParam(dst_device.as<char*>());
|
||||
if (BAD_SHORTADDR == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
||||
|
@ -612,12 +612,12 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
|||
}
|
||||
if (0 == dstLongAddr) { ResponseCmndChar_P(PSTR("Unknown dest IEEE address")); return; }
|
||||
|
||||
const JsonVariant &val_toendpoint = getCaseInsensitive(json, PSTR("ToEndpoint"));
|
||||
const JsonVariant &val_toendpoint = GetCaseInsensitive(json, PSTR("ToEndpoint"));
|
||||
if (nullptr != &val_toendpoint) { toendpoint = strToUInt(val_endpoint); } else { toendpoint = endpoint; }
|
||||
}
|
||||
|
||||
// Or Group Address - we don't need a dstEndpoint in this case
|
||||
const JsonVariant &to_group = getCaseInsensitive(json, PSTR("ToGroup"));
|
||||
const JsonVariant &to_group = GetCaseInsensitive(json, PSTR("ToGroup"));
|
||||
if (nullptr != &to_group) { toGroup = strToUInt(to_group); }
|
||||
|
||||
// make sure we don't have conflicting parameters
|
||||
|
@ -907,13 +907,13 @@ void CmndZbRead(void) {
|
|||
size_t attrs_len = 0;
|
||||
uint8_t* attrs = nullptr; // empty string is valid
|
||||
|
||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
|
||||
if (nullptr != &val_device) {
|
||||
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
||||
}
|
||||
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
|
||||
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
|
||||
const JsonVariant &val_group = GetCaseInsensitive(json, PSTR("Group"));
|
||||
if (nullptr != &val_group) {
|
||||
groupaddr = strToUInt(val_group);
|
||||
} else { // no device nor group
|
||||
|
@ -922,14 +922,14 @@ void CmndZbRead(void) {
|
|||
}
|
||||
}
|
||||
|
||||
const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
|
||||
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR("Cluster"));
|
||||
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
||||
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
|
||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
|
||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||
const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf"));
|
||||
const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR("Manuf"));
|
||||
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
|
||||
|
||||
const JsonVariant &val_attr = getCaseInsensitive(json, PSTR("Read"));
|
||||
const JsonVariant &val_attr = GetCaseInsensitive(json, PSTR("Read"));
|
||||
if (nullptr != &val_attr) {
|
||||
uint16_t val = strToUInt(val_attr);
|
||||
if (val_attr.is<JsonArray>()) {
|
||||
|
@ -1034,21 +1034,21 @@ void CmndZbConfig(void) {
|
|||
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
||||
|
||||
// Channel
|
||||
const JsonVariant &val_channel = getCaseInsensitive(json, PSTR("Channel"));
|
||||
const JsonVariant &val_channel = GetCaseInsensitive(json, PSTR("Channel"));
|
||||
if (nullptr != &val_channel) { zb_channel = strToUInt(val_channel); }
|
||||
if (zb_channel < 11) { zb_channel = 11; }
|
||||
if (zb_channel > 26) { zb_channel = 26; }
|
||||
// PanID
|
||||
const JsonVariant &val_pan_id = getCaseInsensitive(json, PSTR("PanID"));
|
||||
const JsonVariant &val_pan_id = GetCaseInsensitive(json, PSTR("PanID"));
|
||||
if (nullptr != &val_pan_id) { zb_pan_id = strToUInt(val_pan_id); }
|
||||
// ExtPanID
|
||||
const JsonVariant &val_ext_pan_id = getCaseInsensitive(json, PSTR("ExtPanID"));
|
||||
const JsonVariant &val_ext_pan_id = GetCaseInsensitive(json, PSTR("ExtPanID"));
|
||||
if (nullptr != &val_ext_pan_id) { zb_ext_panid = strtoull(val_ext_pan_id.as<const char*>(), nullptr, 0); }
|
||||
// KeyL
|
||||
const JsonVariant &val_key_l = getCaseInsensitive(json, PSTR("KeyL"));
|
||||
const JsonVariant &val_key_l = GetCaseInsensitive(json, PSTR("KeyL"));
|
||||
if (nullptr != &val_key_l) { zb_precfgkey_l = strtoull(val_key_l.as<const char*>(), nullptr, 0); }
|
||||
// KeyH
|
||||
const JsonVariant &val_key_h = getCaseInsensitive(json, PSTR("KeyH"));
|
||||
const JsonVariant &val_key_h = GetCaseInsensitive(json, PSTR("KeyH"));
|
||||
if (nullptr != &val_key_h) { zb_precfgkey_h = strtoull(val_key_h.as<const char*>(), nullptr, 0); }
|
||||
|
||||
// Check if a parameter was changed after all
|
||||
|
|
|
@ -118,12 +118,14 @@ void Sgp30Show(bool json)
|
|||
{
|
||||
if (sgp30_ready) {
|
||||
char abs_hum[33];
|
||||
|
||||
|
||||
if (global_update && global_humidity>0 && global_temperature!=9999) {
|
||||
// has humidity + temperature
|
||||
dtostrfd(sgp30_abshum,4,abs_hum);
|
||||
}
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"SGP30\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d"), sgp.eCO2, sgp.TVOC);
|
||||
if (global_update && global_humidity>0 && global_temperature!=9999) {
|
||||
// has humidity + temperature
|
||||
dtostrfd(sgp30_abshum,4,abs_hum);
|
||||
ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum);
|
||||
}
|
||||
ResponseJsonEnd();
|
||||
|
|
Loading…
Reference in New Issue